From nobody Fri Dec 19 20:51:00 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C15A13839C; Mon, 5 Aug 2024 13:30:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864616; cv=none; b=a1Ka9eSBwE3r6KRsBgRtiYaZ/xpz5PgxzUH0MP1fFh9mRe8Wgme7YYwHZkAEtO9p7EeFsKSbXqU8suwqMUVpKiYg/OosGs1VDAcNnyoQEp54I4ppbyHtOe5CRjQfGLt/eM/IbtZJjaQ8fw+5STKNiIChCIIqbveVSxl9skoqgow= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864616; c=relaxed/simple; bh=tIpUybnkJuGkBeyrchLw+HZ0a/pK0EwFFjPScR16f0k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rFz3pnVs84Xz5OWkoxizoyTYwre4dv0g2VI61Uu7IzhUMo9h9JX8WMe4szlpuJ9AH5LgeNSLUdkXFu9I7pWC+vzInYtMHVRBSZTOTqZcB0KIPdlFeXWK/+msr8yKpm5g5URgpja+kh4QjZl1ujIoqliG++rIHQ7xQdUUfW58dVU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=X05JRkL0; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="X05JRkL0" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9944DC4AF0D; Mon, 5 Aug 2024 13:30:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722864616; bh=tIpUybnkJuGkBeyrchLw+HZ0a/pK0EwFFjPScR16f0k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X05JRkL0rAlViLJ5vLqPs8j8QO+Uz7dB8IGqvXba9phKElaQ7gviC/CLF5/cM6q1f uuywgKLFsiJu6ONjNmlu5q9FbYCiSl7b21UNPhWynv7jKy7ebH6pcgbTVd8vaow6F9 uLNV8htPz70DL9FwsCWx1N+Uw1xNXXJl8NzQ4w04m4w7HiTHeNf/gDnth0oway7Mi4 NBmm2txaViCJsZ5SrVnGRRe0s2sznUw7OyWcB2wwm1WoxkG4bxGBU9wimUrzGflvMS Ft+QW0M35le5ZnPk4sEZ2Ie+uxuBD9vx55cy9XFXpZyWNikpaj+FxAMT42JSuORYYK t/YMlwd9/ME2w== From: "Alexey Gladkov (Intel)" To: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev Cc: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , "Kirill A. Shutemov" , Andrew Morton , Yuan Yao , Geert Uytterhoeven , Yuntao Wang , Kai Huang , Baoquan He , Oleg Nesterov , cho@microsoft.com, decui@microsoft.com, John.Starks@microsoft.com Subject: [PATCH v2 1/5] x86/tdx: Split MMIO read and write operations Date: Mon, 5 Aug 2024 15:29:42 +0200 Message-ID: <0b5f95e9c7639ddb6e4cbd99b3904e6538e91371.1722862355.git.legion@kernel.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" To implement MMIO in userspace, additional memory checks need to be implemented. To avoid overly complicating the handle_mmio() function and to separate checks from actions, it would be better to split this function into two separate functions to handle read and write operations. Signed-off-by: Alexey Gladkov (Intel) --- arch/x86/coco/tdx/tdx.c | 136 ++++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 53 deletions(-) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 078e2bac2553..af0b6c1cacf7 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -405,14 +405,91 @@ static bool mmio_write(int size, unsigned long addr, = unsigned long val) EPT_WRITE, addr, val); } =20 +static int handle_mmio_write(struct insn *insn, enum insn_mmio_type mmio, = int size, + struct pt_regs *regs, struct ve_info *ve) +{ + unsigned long *reg, val; + + switch (mmio) { + case INSN_MMIO_WRITE: + reg =3D insn_get_modrm_reg_ptr(insn, regs); + if (!reg) + return -EINVAL; + memcpy(&val, reg, size); + if (!mmio_write(size, ve->gpa, val)) + return -EIO; + return insn->length; + case INSN_MMIO_WRITE_IMM: + val =3D insn->immediate.value; + if (!mmio_write(size, ve->gpa, val)) + return -EIO; + return insn->length; + case INSN_MMIO_MOVS: + /* + * MMIO was accessed with an instruction that could not be + * decoded or handled properly. It was likely not using io.h + * helpers or accessed MMIO accidentally. + */ + return -EINVAL; + default: + WARN_ON_ONCE(1); + return -EINVAL; + } + + return insn->length; +} + +static int handle_mmio_read(struct insn *insn, enum insn_mmio_type mmio, i= nt size, + struct pt_regs *regs, struct ve_info *ve) +{ + unsigned long *reg, val; + int extend_size; + u8 extend_val; + + reg =3D insn_get_modrm_reg_ptr(insn, regs); + if (!reg) + return -EINVAL; + + if (!mmio_read(size, ve->gpa, &val)) + return -EIO; + + extend_val =3D 0; + + switch (mmio) { + case INSN_MMIO_READ: + /* Zero-extend for 32-bit operation */ + extend_size =3D size =3D=3D 4 ? sizeof(*reg) : 0; + break; + case INSN_MMIO_READ_ZERO_EXTEND: + /* Zero extend based on operand size */ + extend_size =3D insn->opnd_bytes; + break; + case INSN_MMIO_READ_SIGN_EXTEND: + /* Sign extend based on operand size */ + extend_size =3D insn->opnd_bytes; + if (size =3D=3D 1 && val & BIT(7)) + extend_val =3D 0xFF; + else if (size > 1 && val & BIT(15)) + extend_val =3D 0xFF; + break; + default: + WARN_ON_ONCE(1); + return -EINVAL; + } + + if (extend_size) + memset(reg, extend_val, extend_size); + memcpy(reg, &val, size); + return insn->length; +} + static int handle_mmio(struct pt_regs *regs, struct ve_info *ve) { - unsigned long *reg, val, vaddr; char buffer[MAX_INSN_SIZE]; enum insn_mmio_type mmio; struct insn insn =3D {}; - int size, extend_size; - u8 extend_val =3D 0; + unsigned long vaddr; + int size; =20 /* Only in-kernel MMIO is supported */ if (WARN_ON_ONCE(user_mode(regs))) @@ -428,12 +505,6 @@ static int handle_mmio(struct pt_regs *regs, struct ve= _info *ve) if (WARN_ON_ONCE(mmio =3D=3D INSN_MMIO_DECODE_FAILED)) return -EINVAL; =20 - if (mmio !=3D INSN_MMIO_WRITE_IMM && mmio !=3D INSN_MMIO_MOVS) { - reg =3D insn_get_modrm_reg_ptr(&insn, regs); - if (!reg) - return -EINVAL; - } - /* * Reject EPT violation #VEs that split pages. * @@ -447,24 +518,15 @@ static int handle_mmio(struct pt_regs *regs, struct v= e_info *ve) if (vaddr / PAGE_SIZE !=3D (vaddr + size - 1) / PAGE_SIZE) return -EFAULT; =20 - /* Handle writes first */ switch (mmio) { case INSN_MMIO_WRITE: - memcpy(&val, reg, size); - if (!mmio_write(size, ve->gpa, val)) - return -EIO; - return insn.length; case INSN_MMIO_WRITE_IMM: - val =3D insn.immediate.value; - if (!mmio_write(size, ve->gpa, val)) - return -EIO; - return insn.length; + case INSN_MMIO_MOVS: + return handle_mmio_write(&insn, mmio, size, regs, ve); case INSN_MMIO_READ: case INSN_MMIO_READ_ZERO_EXTEND: case INSN_MMIO_READ_SIGN_EXTEND: - /* Reads are handled below */ - break; - case INSN_MMIO_MOVS: + return handle_mmio_read(&insn, mmio, size, regs, ve); case INSN_MMIO_DECODE_FAILED: /* * MMIO was accessed with an instruction that could not be @@ -476,38 +538,6 @@ static int handle_mmio(struct pt_regs *regs, struct ve= _info *ve) WARN_ONCE(1, "Unknown insn_decode_mmio() decode value?"); return -EINVAL; } - - /* Handle reads */ - if (!mmio_read(size, ve->gpa, &val)) - return -EIO; - - switch (mmio) { - case INSN_MMIO_READ: - /* Zero-extend for 32-bit operation */ - extend_size =3D size =3D=3D 4 ? sizeof(*reg) : 0; - break; - case INSN_MMIO_READ_ZERO_EXTEND: - /* Zero extend based on operand size */ - extend_size =3D insn.opnd_bytes; - break; - case INSN_MMIO_READ_SIGN_EXTEND: - /* Sign extend based on operand size */ - extend_size =3D insn.opnd_bytes; - if (size =3D=3D 1 && val & BIT(7)) - extend_val =3D 0xFF; - else if (size > 1 && val & BIT(15)) - extend_val =3D 0xFF; - break; - default: - /* All other cases has to be covered with the first switch() */ - WARN_ON_ONCE(1); - return -EINVAL; - } - - if (extend_size) - memset(reg, extend_val, extend_size); - memcpy(reg, &val, size); - return insn.length; } =20 static bool handle_in(struct pt_regs *regs, int size, int port) --=20 2.45.2 From nobody Fri Dec 19 20:51:00 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C19453839C; Mon, 5 Aug 2024 13:30:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864620; cv=none; b=C3MkVp+YvvY1Z2wH8sEX10DSdB+Fd4jf+rFiKA1LZPzO4wRXd3KPi30ifkoXsPm63JTgFtEz9/rJ52QTfmRb9i2YLcKT4c1hjq8NdpgmzqUz+lPx55THTJMhv/PdA7P/8tPIir47CV36hSDTDzpaLKXCWktzA6xXakeli1CiMvw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864620; c=relaxed/simple; bh=jvzpEDQxQeNDmwcUGKDpmc1NCKPIdKJclCjV+OVjxGs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lS6rXCsnQPlUV6UGcYl4pDn/3DkZLT115T/JGvmERK7nZtYs8q2H/QgTrz782JhALvFubcFT6OakG1OGUzEqyX+ZEBXbU/phGTqhMVnBN50aqtoMmxZd4C6d4xNG+9ziJo0UN8uCbF2nb47Ft8r/eR9vuz4tJzZ8ZMbnEF6Yyc4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=etfsJ5rN; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="etfsJ5rN" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B5821C32782; Mon, 5 Aug 2024 13:30:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722864620; bh=jvzpEDQxQeNDmwcUGKDpmc1NCKPIdKJclCjV+OVjxGs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=etfsJ5rNXCceuoiIZmrGdyJmmHN6maFyLO6bqhvPlOqHK5hq3H2s3QIdsC92+hsB8 umBF+hu4AS7FKqAT43Rw29+DZQcirt4EpY6QklKOTXAF0y82HYKmqic5qkZextpjf7 ovh6Bjz7pc7c/GQH73/mEag2uSwQcBViuGn89XUH5J9w7QNq22RakwWJYjTc86WmS9 8cJu9yqbMSfgoF1nxpXWb4FE5skVJD5OUQ3+l6OdRKRSPhWkz84Xt21egjBjw7jPUO WYIrcry3AuNfE+sI6ndbbbHXHGaSg1Twd4e+nGxF/hyi2sxKTo/yXF//dnrF+v8z0I zCXJdUCBb25xQ== From: "Alexey Gladkov (Intel)" To: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev Cc: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , "Kirill A. Shutemov" , Andrew Morton , Yuan Yao , Geert Uytterhoeven , Yuntao Wang , Kai Huang , Baoquan He , Oleg Nesterov , cho@microsoft.com, decui@microsoft.com, John.Starks@microsoft.com Subject: [PATCH v2 2/5] x86/tdx: Add validation of userspace MMIO instructions Date: Mon, 5 Aug 2024 15:29:43 +0200 Message-ID: <6f989aea155817ef2f8a5fd2240ccff3f74d4edd.1722862355.git.legion@kernel.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Instructions from kernel space are considered trusted. If the MMIO instruction is from userspace it must be checked. For userspace instructions, it is need to check that the INSN has not changed at the time of #VE and before the execution of the instruction. Once the userspace instruction parsed is enforced that the address points to mapped memory of current process and that address does not point to private memory. After parsing the userspace instruction, it is necessary to ensure that: 1. the operation direction (read/write) corresponds to #VE info; 2. the address still points to mapped memory of current process; 3. the address does not point to private memory. Signed-off-by: Alexey Gladkov (Intel) --- arch/x86/coco/tdx/tdx.c | 128 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 13 deletions(-) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index af0b6c1cacf7..95f2ff49728c 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -405,6 +406,84 @@ static bool mmio_write(int size, unsigned long addr, u= nsigned long val) EPT_WRITE, addr, val); } =20 +static inline bool is_private_gpa(u64 gpa) +{ + return gpa =3D=3D cc_mkenc(gpa); +} + +static int get_phys_addr(unsigned long addr, phys_addr_t *phys_addr, bool = *writable) +{ + unsigned int level; + pgd_t *pgdp; + pte_t *ptep; + + /* + * Address validation only makes sense for a user process. The lock must + * be obtained before validation can begin. + */ + mmap_assert_locked(current->mm); + + pgdp =3D pgd_offset(current->mm, addr); + + if (!pgd_none(*pgdp)) { + ptep =3D lookup_address_in_pgd(pgdp, addr, &level); + if (ptep) { + unsigned long offset; + + offset =3D addr & ~page_level_mask(level); + *phys_addr =3D PFN_PHYS(pte_pfn(*ptep)); + *phys_addr |=3D offset; + + *writable =3D pte_write(*ptep); + + return 0; + } + } + + return -EFAULT; +} + +static int valid_vaddr(struct ve_info *ve, enum insn_mmio_type mmio, int s= ize, + unsigned long vaddr) +{ + phys_addr_t phys_addr; + bool writable =3D false; + + /* It's not fatal. This can happen due to swap out or page migration. */ + if (get_phys_addr(vaddr, &phys_addr, &writable) || (ve->gpa !=3D cc_mkdec= (phys_addr))) + return -EAGAIN; + + /* + * Re-check whether #VE info matches the instruction that was decoded. + * + * The ve->gpa was valid at the time ve_info was received. But this code + * executed with interrupts enabled, allowing tlb shootdown and therefore + * munmap() to be executed in the parallel thread. + * + * By the time MMIO emulation is performed, ve->gpa may be already + * unmapped from the process, the device it belongs to removed from + * system and something else could be plugged in its place. + */ + switch (mmio) { + case INSN_MMIO_WRITE: + case INSN_MMIO_WRITE_IMM: + if (!writable || !(ve->exit_qual & EPT_VIOLATION_ACC_WRITE)) + return -EFAULT; + break; + case INSN_MMIO_READ: + case INSN_MMIO_READ_ZERO_EXTEND: + case INSN_MMIO_READ_SIGN_EXTEND: + if (!(ve->exit_qual & EPT_VIOLATION_ACC_READ)) + return -EFAULT; + break; + default: + WARN_ONCE(1, "Unsupported mmio instruction: %d", mmio); + return -EINVAL; + } + + return 0; +} + static int handle_mmio_write(struct insn *insn, enum insn_mmio_type mmio, = int size, struct pt_regs *regs, struct ve_info *ve) { @@ -489,7 +568,7 @@ static int handle_mmio(struct pt_regs *regs, struct ve_= info *ve) enum insn_mmio_type mmio; struct insn insn =3D {}; unsigned long vaddr; - int size; + int size, ret; =20 /* Only in-kernel MMIO is supported */ if (WARN_ON_ONCE(user_mode(regs))) @@ -505,6 +584,17 @@ static int handle_mmio(struct pt_regs *regs, struct ve= _info *ve) if (WARN_ON_ONCE(mmio =3D=3D INSN_MMIO_DECODE_FAILED)) return -EINVAL; =20 + vaddr =3D (unsigned long)insn_get_addr_ref(&insn, regs); + + if (user_mode(regs)) { + if (mmap_read_lock_killable(current->mm)) + return -EINTR; + + ret =3D valid_vaddr(ve, mmio, size, vaddr); + if (ret) + goto unlock; + } + /* * Reject EPT violation #VEs that split pages. * @@ -514,30 +604,39 @@ static int handle_mmio(struct pt_regs *regs, struct v= e_info *ve) * * load_unaligned_zeropad() will recover using exception fixups. */ - vaddr =3D (unsigned long)insn_get_addr_ref(&insn, regs); - if (vaddr / PAGE_SIZE !=3D (vaddr + size - 1) / PAGE_SIZE) - return -EFAULT; + if (vaddr / PAGE_SIZE !=3D (vaddr + size - 1) / PAGE_SIZE) { + ret =3D -EFAULT; + goto unlock; + } =20 switch (mmio) { case INSN_MMIO_WRITE: case INSN_MMIO_WRITE_IMM: case INSN_MMIO_MOVS: - return handle_mmio_write(&insn, mmio, size, regs, ve); + ret =3D handle_mmio_write(&insn, mmio, size, regs, ve); + break; case INSN_MMIO_READ: case INSN_MMIO_READ_ZERO_EXTEND: case INSN_MMIO_READ_SIGN_EXTEND: - return handle_mmio_read(&insn, mmio, size, regs, ve); + ret =3D handle_mmio_read(&insn, mmio, size, regs, ve); + break; case INSN_MMIO_DECODE_FAILED: /* * MMIO was accessed with an instruction that could not be * decoded or handled properly. It was likely not using io.h * helpers or accessed MMIO accidentally. */ - return -EINVAL; + ret =3D -EINVAL; + break; default: WARN_ONCE(1, "Unknown insn_decode_mmio() decode value?"); - return -EINVAL; + ret =3D -EINVAL; } +unlock: + if (user_mode(regs)) + mmap_read_unlock(current->mm); + + return ret; } =20 static bool handle_in(struct pt_regs *regs, int size, int port) @@ -681,11 +780,6 @@ static int virt_exception_user(struct pt_regs *regs, s= truct ve_info *ve) } } =20 -static inline bool is_private_gpa(u64 gpa) -{ - return gpa =3D=3D cc_mkenc(gpa); -} - /* * Handle the kernel #VE. * @@ -723,6 +817,14 @@ bool tdx_handle_virt_exception(struct pt_regs *regs, s= truct ve_info *ve) insn_len =3D virt_exception_user(regs, ve); else insn_len =3D virt_exception_kernel(regs, ve); + + /* + * A special case to return to userspace without increasing regs->ip + * to repeat the instruction once again. + */ + if (insn_len =3D=3D -EAGAIN) + return true; + if (insn_len < 0) return false; =20 --=20 2.45.2 From nobody Fri Dec 19 20:51:00 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9CDBB41C65; Mon, 5 Aug 2024 13:30:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864624; cv=none; b=MpjKlm6ZYqj49nneFZq+RRs3anxbNbuNwpHUFsiuhyd7PvfCRBSOVjqdBm0YiNwXwfpeuvuZWGOksYeRAJ+eIvyEPBS98UXn6jadpmgQ5hRQIxO7P/4CGbVMS1NHkTOd6myxXGFcqgIJElBk3+d75SR+Ias56zrPqyCHij92wHU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864624; c=relaxed/simple; bh=bZByem6u/WWAKyXoQLMIss6NWXsjus+uY2+EQu/RwbQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=I1yGgPAabvx3jnM0oy83CKeCiIjmsJEeDeUFNImRrRti0lFkE7thoMt3YYXz2zEWGkBrHapuM4v1uLUdF+zASnrYX92VSGpHI3ErNGWWSryMJn//G2sSJJd4Uu+m9DoK0xp0k7xJZyoZcamLIF3SayZplNqyJ6qmEihriEtYqnc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dgXs2uID; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dgXs2uID" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CFA10C4AF0D; Mon, 5 Aug 2024 13:30:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722864624; bh=bZByem6u/WWAKyXoQLMIss6NWXsjus+uY2+EQu/RwbQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dgXs2uIDj2T5U4SJgixwNLnCpoUL/xDW+D4HZq29O26nS+7rahDU1gc1g3NbOJyBU HCZFP6t6Ig+tZnTtQbopJ6C8f/Ve48COmerpmKXiESCLiartZkA9ky73/UjWM8Paa8 XcEbSXoFGnqcatRxylqHJx2ggpJjfqfQGBiJyIRJvy0ivPi1neEqfUPzdl2Z6LdvlH Ra6MsXW1OwqHm6OqLu2psnwFAIBaZAGiGCt8lbeu5WK42yI1xemd8z4FIOfUYCftfE DUtO48b+yWTpkQ4Z63/e0yzngew4nUkqznm/S4l9r+54gxRHJFK4BQ+Gq8oF8k3k7P diKb7m7f0Tn+Q== From: "Alexey Gladkov (Intel)" To: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev Cc: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , "Kirill A. Shutemov" , Andrew Morton , Yuan Yao , Geert Uytterhoeven , Yuntao Wang , Kai Huang , Baoquan He , Oleg Nesterov , cho@microsoft.com, decui@microsoft.com, John.Starks@microsoft.com Subject: [PATCH v2 3/5] x86/tdx: Allow MMIO from userspace Date: Mon, 5 Aug 2024 15:29:44 +0200 Message-ID: X-Mailer: git-send-email 2.45.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The MMIO emulation is only allowed for kernel space code. It is carried out through a special API, which uses only certain instructions. This does not allow userspace to work with virtual devices. Allow userspace to use the same instructions as kernel space to access MMIO. So far, no additional checks have been made. Reviewed-by: Thomas Gleixner Signed-off-by: Alexey Gladkov (Intel) --- arch/x86/coco/tdx/tdx.c | 42 +++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 95f2ff49728c..4e2fb9bf83a1 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -484,6 +484,31 @@ static int valid_vaddr(struct ve_info *ve, enum insn_m= mio_type mmio, int size, return 0; } =20 +static int decode_insn_struct(struct insn *insn, struct pt_regs *regs) +{ + char buffer[MAX_INSN_SIZE]; + + if (user_mode(regs)) { + int nr_copied =3D insn_fetch_from_user(regs, buffer); + + if (nr_copied <=3D 0) + return -EFAULT; + + if (!insn_decode_from_regs(insn, regs, buffer, nr_copied)) + return -EINVAL; + + if (!insn->immediate.got) + return -EINVAL; + } else { + if (copy_from_kernel_nofault(buffer, (void *)regs->ip, MAX_INSN_SIZE)) + return -EFAULT; + + if (insn_decode(insn, buffer, MAX_INSN_SIZE, INSN_MODE_64)) + return -EINVAL; + } + return 0; +} + static int handle_mmio_write(struct insn *insn, enum insn_mmio_type mmio, = int size, struct pt_regs *regs, struct ve_info *ve) { @@ -564,21 +589,14 @@ static int handle_mmio_read(struct insn *insn, enum i= nsn_mmio_type mmio, int siz =20 static int handle_mmio(struct pt_regs *regs, struct ve_info *ve) { - char buffer[MAX_INSN_SIZE]; enum insn_mmio_type mmio; struct insn insn =3D {}; unsigned long vaddr; int size, ret; =20 - /* Only in-kernel MMIO is supported */ - if (WARN_ON_ONCE(user_mode(regs))) - return -EFAULT; - - if (copy_from_kernel_nofault(buffer, (void *)regs->ip, MAX_INSN_SIZE)) - return -EFAULT; - - if (insn_decode(&insn, buffer, MAX_INSN_SIZE, INSN_MODE_64)) - return -EINVAL; + ret =3D decode_insn_struct(&insn, regs); + if (ret) + return ret; =20 mmio =3D insn_decode_mmio(&insn, &size); if (WARN_ON_ONCE(mmio =3D=3D INSN_MMIO_DECODE_FAILED)) @@ -774,6 +792,10 @@ static int virt_exception_user(struct pt_regs *regs, s= truct ve_info *ve) switch (ve->exit_reason) { case EXIT_REASON_CPUID: return handle_cpuid(regs, ve); + case EXIT_REASON_EPT_VIOLATION: + if (is_private_gpa(ve->gpa)) + panic("Unexpected EPT-violation on private memory."); + return handle_mmio(regs, ve); default: pr_warn("Unexpected #VE: %lld\n", ve->exit_reason); return -EIO; --=20 2.45.2 From nobody Fri Dec 19 20:51:00 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0EC2E2F877; Mon, 5 Aug 2024 13:30:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864629; cv=none; b=j/MCln60Xx7+Fw0tXYmXdkB3w0MMOydEyVqzqaxyYDvuNrXvAoHYbuYVcvSO9H0C46mYtv5pgbdz6q8vcy5EizaogW5qSVMB4XosV4Z2r6VZO9PuAOX5GXZQUNRJGHQuVTQc7HxWaHlqTbYJ6a+rEJniH2pr9haMsbdt1n6kS/8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864629; c=relaxed/simple; bh=pftIffm9tqkmIEwvbWnrZ2fTbg+Y+uBXbgF7dzJ1mQQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ae/3SQL2UKfqvT51Tv2FGBsWd8NFGYN9ohJRYuASsEmEotPb6c0Jo+vHqKeS4h9YkkwcVPuJ2LOsXwQDxMZUa4q+F/lTuJ2eKbxhgldxx4eERNFMyNXTLcyH9TrCC6gPt4jyBEad1lhpBRPleJIE/xc6MRt2AIn0MVCLxylRsfc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=U07rlGn1; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="U07rlGn1" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EFCBAC32782; Mon, 5 Aug 2024 13:30:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722864628; bh=pftIffm9tqkmIEwvbWnrZ2fTbg+Y+uBXbgF7dzJ1mQQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=U07rlGn1eUwmtf6jne29RPwjDGPKFpPLjj2KqO6Qmfo7rgguEBAY+A/zorvj2At38 JF1oQ2nlPcjvun8IWafqL1KlzKntfafAPPupVmcVxiWSOJncwsS+Ne4HNF9bjUAEoR 9kFg4L52FeZOD/Gr8NqT9vKNPMsasKaKKbsff169EmrTLyKNT9mClKmKMoaPyi9RIQ mIDq6pAjIMBrMjqL/X7KbNs3Ozuotj8574pyBq7tXsUaEKzs2Qr+Sv4izqwBuKeML/ fjbgP7HprVoDbXaMcUQrDlUnTzlzA+eB5cPJ3YUhcpBGSDfqi/OyMHvCbfv5MYEACP OhPbU6mBGHdRw== From: "Alexey Gladkov (Intel)" To: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev Cc: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , "Kirill A. Shutemov" , Andrew Morton , Yuan Yao , Geert Uytterhoeven , Yuntao Wang , Kai Huang , Baoquan He , Oleg Nesterov , cho@microsoft.com, decui@microsoft.com, John.Starks@microsoft.com Subject: [PATCH v2 4/5] x86/tdx: Move MMIO helpers to common library Date: Mon, 5 Aug 2024 15:29:45 +0200 Message-ID: <3ee4cc00684dcba601da2a75ef54c36c8b0da4ef.1722862355.git.legion@kernel.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" AMD code has helpers that are used to emulate MOVS instructions. To be able to reuse this code in the MOVS implementation for intel, it is necessary to move them to a common location. Signed-off-by: Alexey Gladkov (Intel) --- arch/x86/coco/sev/core.c | 135 ++++---------------------------------- arch/x86/include/asm/io.h | 3 + arch/x86/lib/iomem.c | 125 +++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 121 deletions(-) diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c index 082d61d85dfc..0e10c22c5347 100644 --- a/arch/x86/coco/sev/core.c +++ b/arch/x86/coco/sev/core.c @@ -369,72 +369,18 @@ static enum es_result vc_decode_insn(struct es_em_ctx= t *ctxt) static enum es_result vc_write_mem(struct es_em_ctxt *ctxt, char *dst, char *buf, size_t size) { - unsigned long error_code =3D X86_PF_PROT | X86_PF_WRITE; - - /* - * This function uses __put_user() independent of whether kernel or user - * memory is accessed. This works fine because __put_user() does no - * sanity checks of the pointer being accessed. All that it does is - * to report when the access failed. - * - * Also, this function runs in atomic context, so __put_user() is not - * allowed to sleep. The page-fault handler detects that it is running - * in atomic context and will not try to take mmap_sem and handle the - * fault, so additional pagefault_enable()/disable() calls are not - * needed. - * - * The access can't be done via copy_to_user() here because - * vc_write_mem() must not use string instructions to access unsafe - * memory. The reason is that MOVS is emulated by the #VC handler by - * splitting the move up into a read and a write and taking a nested #VC - * exception on whatever of them is the MMIO access. Using string - * instructions here would cause infinite nesting. - */ - switch (size) { - case 1: { - u8 d1; - u8 __user *target =3D (u8 __user *)dst; - - memcpy(&d1, buf, 1); - if (__put_user(d1, target)) - goto fault; - break; - } - case 2: { - u16 d2; - u16 __user *target =3D (u16 __user *)dst; - - memcpy(&d2, buf, 2); - if (__put_user(d2, target)) - goto fault; - break; - } - case 4: { - u32 d4; - u32 __user *target =3D (u32 __user *)dst; + unsigned long error_code; + int ret; =20 - memcpy(&d4, buf, 4); - if (__put_user(d4, target)) - goto fault; - break; - } - case 8: { - u64 d8; - u64 __user *target =3D (u64 __user *)dst; + ret =3D __put_iomem(dst, buf, size); + if (!ret) + return ES_OK; =20 - memcpy(&d8, buf, 8); - if (__put_user(d8, target)) - goto fault; - break; - } - default: - WARN_ONCE(1, "%s: Invalid size: %zu\n", __func__, size); + if (ret =3D=3D -EIO) return ES_UNSUPPORTED; - } =20 - return ES_OK; + error_code =3D X86_PF_PROT | X86_PF_WRITE; =20 -fault: if (user_mode(ctxt->regs)) error_code |=3D X86_PF_USER; =20 @@ -448,71 +394,18 @@ static enum es_result vc_write_mem(struct es_em_ctxt = *ctxt, static enum es_result vc_read_mem(struct es_em_ctxt *ctxt, char *src, char *buf, size_t size) { - unsigned long error_code =3D X86_PF_PROT; - - /* - * This function uses __get_user() independent of whether kernel or user - * memory is accessed. This works fine because __get_user() does no - * sanity checks of the pointer being accessed. All that it does is - * to report when the access failed. - * - * Also, this function runs in atomic context, so __get_user() is not - * allowed to sleep. The page-fault handler detects that it is running - * in atomic context and will not try to take mmap_sem and handle the - * fault, so additional pagefault_enable()/disable() calls are not - * needed. - * - * The access can't be done via copy_from_user() here because - * vc_read_mem() must not use string instructions to access unsafe - * memory. The reason is that MOVS is emulated by the #VC handler by - * splitting the move up into a read and a write and taking a nested #VC - * exception on whatever of them is the MMIO access. Using string - * instructions here would cause infinite nesting. - */ - switch (size) { - case 1: { - u8 d1; - u8 __user *s =3D (u8 __user *)src; - - if (__get_user(d1, s)) - goto fault; - memcpy(buf, &d1, 1); - break; - } - case 2: { - u16 d2; - u16 __user *s =3D (u16 __user *)src; + unsigned long error_code; + int ret; =20 - if (__get_user(d2, s)) - goto fault; - memcpy(buf, &d2, 2); - break; - } - case 4: { - u32 d4; - u32 __user *s =3D (u32 __user *)src; + ret =3D __get_iomem(src, buf, size); + if (!ret) + return ES_OK; =20 - if (__get_user(d4, s)) - goto fault; - memcpy(buf, &d4, 4); - break; - } - case 8: { - u64 d8; - u64 __user *s =3D (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); + if (ret =3D=3D -EIO) return ES_UNSUPPORTED; - } =20 - return ES_OK; + error_code =3D X86_PF_PROT; =20 -fault: if (user_mode(ctxt->regs)) error_code |=3D X86_PF_USER; =20 diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h index 1d60427379c9..ac01d53466cb 100644 --- a/arch/x86/include/asm/io.h +++ b/arch/x86/include/asm/io.h @@ -402,4 +402,7 @@ static inline void iosubmit_cmds512(void __iomem *dst, = const void *src, } } =20 +int __get_iomem(char *src, char *buf, size_t size); +int __put_iomem(char *src, char *buf, size_t size); + #endif /* _ASM_X86_IO_H */ diff --git a/arch/x86/lib/iomem.c b/arch/x86/lib/iomem.c index 5eecb45d05d5..23179953eb5a 100644 --- a/arch/x86/lib/iomem.c +++ b/arch/x86/lib/iomem.c @@ -2,6 +2,7 @@ #include #include #include +#include =20 #define movs(type,to,from) \ asm volatile("movs" type:"=3D&D" (to), "=3D&S" (from):"0" (to), "1" (from= ):"memory") @@ -124,3 +125,127 @@ void memset_io(volatile void __iomem *a, int b, size_= t c) } } EXPORT_SYMBOL(memset_io); + +int __get_iomem(char *src, char *buf, size_t size) +{ + /* + * This function uses __get_user() independent of whether kernel or user + * memory is accessed. This works fine because __get_user() does no + * sanity checks of the pointer being accessed. All that it does is + * to report when the access failed. + * + * Also, this function runs in atomic context, so __get_user() is not + * allowed to sleep. The page-fault handler detects that it is running + * in atomic context and will not try to take mmap_sem and handle the + * fault, so additional pagefault_enable()/disable() calls are not + * needed. + * + * The access can't be done via copy_from_user() here because + * mmio_read_mem() must not use string instructions to access unsafe + * memory. The reason is that MOVS is emulated by the #VC handler by + * splitting the move up into a read and a write and taking a nested #VC + * exception on whatever of them is the MMIO access. Using string + * instructions here would cause infinite nesting. + */ + switch (size) { + case 1: { + u8 d1, __user *s =3D (u8 __user *)src; + + if (__get_user(d1, s)) + return -EFAULT; + memcpy(buf, &d1, 1); + break; + } + case 2: { + u16 d2, __user *s =3D (u16 __user *)src; + + if (__get_user(d2, s)) + return -EFAULT; + memcpy(buf, &d2, 2); + break; + } + case 4: { + u32 d4, __user *s =3D (u32 __user *)src; + + if (__get_user(d4, s)) + return -EFAULT; + memcpy(buf, &d4, 4); + break; + } + case 8: { + u64 d8, __user *s =3D (u64 __user *)src; + + if (__get_user(d8, s)) + return -EFAULT; + memcpy(buf, &d8, 8); + break; + } + default: + WARN_ONCE(1, "%s: Invalid size: %zu\n", __func__, size); + return -EIO; + } + + return 0; +} + +int __put_iomem(char *dst, char *buf, size_t size) +{ + /* + * This function uses __put_user() independent of whether kernel or user + * memory is accessed. This works fine because __put_user() does no + * sanity checks of the pointer being accessed. All that it does is + * to report when the access failed. + * + * Also, this function runs in atomic context, so __put_user() is not + * allowed to sleep. The page-fault handler detects that it is running + * in atomic context and will not try to take mmap_sem and handle the + * fault, so additional pagefault_enable()/disable() calls are not + * needed. + * + * The access can't be done via copy_to_user() here because + * put_iomem() must not use string instructions to access unsafe + * memory. The reason is that MOVS is emulated by the #VC handler by + * splitting the move up into a read and a write and taking a nested #VC + * exception on whatever of them is the MMIO access. Using string + * instructions here would cause infinite nesting. + */ + switch (size) { + case 1: { + u8 d1, __user *target =3D (u8 __user *)dst; + + memcpy(&d1, buf, 1); + if (__put_user(d1, target)) + return -EFAULT; + break; + } + case 2: { + u16 d2, __user *target =3D (u16 __user *)dst; + + memcpy(&d2, buf, 2); + if (__put_user(d2, target)) + return -EFAULT; + break; + } + case 4: { + u32 d4, __user *target =3D (u32 __user *)dst; + + memcpy(&d4, buf, 4); + if (__put_user(d4, target)) + return -EFAULT; + break; + } + case 8: { + u64 d8, __user *target =3D (u64 __user *)dst; + + memcpy(&d8, buf, 8); + if (__put_user(d8, target)) + return -EFAULT; + break; + } + default: + WARN_ONCE(1, "%s: Invalid size: %zu\n", __func__, size); + return -EIO; + } + + return 0; +} --=20 2.45.2 From nobody Fri Dec 19 20:51:00 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2CDD544C7C; Mon, 5 Aug 2024 13:30:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864633; cv=none; b=aisnuhqpnr1rv8D0y8Lkde+XGSRGdoDutf6V4xVFa+EYzt2Osq9IXjVUogLNmdeRwnzDmKG9QlWGku2DVnFJ/8RZnuSVzrn8u3gseWRcf81tau5e9DAkC66jz7kuPRNkfFouLMFXvVubqzjnzn2MqnnoPT7lRex0CfpgHXY+qSM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722864633; c=relaxed/simple; bh=HebbKfNaui0e5CTtw/jnZCkv3vdS2QE7Ew+P8EfTDVs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=twGIYwfneAIBuzQL0ri3wH3Uf/M8x10GSk7T0GQg/BGdFPPwFS2h46zaa6WSlS00RzFU/gt+bNzV6fEVLNjpjVHky4tumTGhzqNcV2ZbpFBqgNofv6mvxgtJ29dEGA63WdERkmKGRUHJ8NrFQ4XYmSdqN7byIoi+2Xo/S7Z4h8c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FAqbFiE7; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FAqbFiE7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 16432C4AF0E; Mon, 5 Aug 2024 13:30:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722864632; bh=HebbKfNaui0e5CTtw/jnZCkv3vdS2QE7Ew+P8EfTDVs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FAqbFiE7CBcd/+K0P5YLLczF+7n0N7WuLsblk3LgpNUUzudxVArkE2HjxWShzX/WX KP9uKaT50uQLkuPSlb3E6vZplFEoKLVWWBxzlrycm990Re/B7kl/MKFgzHGNRBXnyB bU08nD7+tc6nd9PY21UcP8wIURfwIQjvKZpWJNynfXKjnmyCgC2C9w8mQuB2Rg/IGG NhDOnkTis3YYMcD785yJTeL7YGYA9MSoVr2GAukqqgRVixlqQnkiY4DNpIR2Oc4pc8 xVHSXjVzkZhFzna502iFc+4U7Nbrp9pu0pTKvh/8M1uV9uhjmo1JrQzhyuVbGjxe64 XSkFp/7Shrn5Q== From: "Alexey Gladkov (Intel)" To: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev Cc: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , "Kirill A. Shutemov" , Andrew Morton , Yuan Yao , Geert Uytterhoeven , Yuntao Wang , Kai Huang , Baoquan He , Oleg Nesterov , cho@microsoft.com, decui@microsoft.com, John.Starks@microsoft.com Subject: [PATCH v2 5/5] x86/tdx: Implement movs for MMIO Date: Mon, 5 Aug 2024 15:29:46 +0200 Message-ID: <83aa03c8f95ef00a6cf2fd6fa768c4b13e533d1c.1722862355.git.legion@kernel.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add emulation of the MOVS instruction on MMIO regions. MOVS emulation consists of dividing it into a series of read and write operations, which in turn will be validated separately. Signed-off-by: Alexey Gladkov (Intel) --- arch/x86/coco/tdx/tdx.c | 57 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 4e2fb9bf83a1..8573cb23837e 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -509,6 +509,54 @@ static int decode_insn_struct(struct insn *insn, struc= t pt_regs *regs) return 0; } =20 +static int handle_mmio_movs(struct insn *insn, struct pt_regs *regs, int s= ize, struct ve_info *ve) +{ + unsigned long ds_base, es_base; + unsigned char *src, *dst; + unsigned char buffer[8]; + int off, ret; + bool rep; + + /* + * The in-kernel code must use a special API that does not use MOVS. + * If the MOVS instruction is received from in-kernel, then something + * is broken. + */ + if (WARN_ON_ONCE(!user_mode(regs))) + return -EFAULT; + + ds_base =3D insn_get_seg_base(regs, INAT_SEG_REG_DS); + es_base =3D insn_get_seg_base(regs, INAT_SEG_REG_ES); + + if (ds_base =3D=3D -1L || es_base =3D=3D -1L) + return -EINVAL; + + rep =3D insn_has_rep_prefix(insn); + + do { + src =3D ds_base + (unsigned char *) regs->si; + dst =3D es_base + (unsigned char *) regs->di; + + ret =3D __get_iomem(src, buffer, size); + if (ret) + return ret; + + ret =3D __put_iomem(dst, buffer, size); + if (ret) + return ret; + + off =3D (regs->flags & X86_EFLAGS_DF) ? -size : size; + + regs->si +=3D off; + regs->di +=3D off; + + if (rep) + regs->cx -=3D 1; + } while (rep || regs->cx > 0); + + return insn->length; +} + static int handle_mmio_write(struct insn *insn, enum insn_mmio_type mmio, = int size, struct pt_regs *regs, struct ve_info *ve) { @@ -530,9 +578,8 @@ static int handle_mmio_write(struct insn *insn, enum in= sn_mmio_type mmio, int si return insn->length; case INSN_MMIO_MOVS: /* - * MMIO was accessed with an instruction that could not be - * decoded or handled properly. It was likely not using io.h - * helpers or accessed MMIO accidentally. + * MOVS is processed through higher level emulation which breaks + * this instruction into a sequence of reads and writes. */ return -EINVAL; default: @@ -602,6 +649,9 @@ static int handle_mmio(struct pt_regs *regs, struct ve_= info *ve) if (WARN_ON_ONCE(mmio =3D=3D INSN_MMIO_DECODE_FAILED)) return -EINVAL; =20 + if (mmio =3D=3D INSN_MMIO_MOVS) + return handle_mmio_movs(&insn, regs, size, ve); + vaddr =3D (unsigned long)insn_get_addr_ref(&insn, regs); =20 if (user_mode(regs)) { @@ -630,7 +680,6 @@ static int handle_mmio(struct pt_regs *regs, struct ve_= info *ve) switch (mmio) { case INSN_MMIO_WRITE: case INSN_MMIO_WRITE_IMM: - case INSN_MMIO_MOVS: ret =3D handle_mmio_write(&insn, mmio, size, regs, ve); break; case INSN_MMIO_READ: --=20 2.45.2