mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 08:44:41 +00:00 
			
		
		
		
	 73d49df2c3
			
		
	
	
		73d49df2c3
		
	
	
	
	
		
			
			Add a stage-2 wrprotect() operation to the generic page-table code. Signed-off-by: Quentin Perret <qperret@google.com> Signed-off-by: Will Deacon <will@kernel.org> Signed-off-by: Marc Zyngier <maz@kernel.org> Reviewed-by: Gavin Shan <gshan@redhat.com> Cc: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20200911132529.19844-13-will@kernel.org
		
			
				
	
	
		
			275 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-only
 | |
| /*
 | |
|  * Copyright (C) 2020 Google LLC
 | |
|  * Author: Will Deacon <will@kernel.org>
 | |
|  */
 | |
| 
 | |
| #ifndef __ARM64_KVM_PGTABLE_H__
 | |
| #define __ARM64_KVM_PGTABLE_H__
 | |
| 
 | |
| #include <linux/bits.h>
 | |
| #include <linux/kvm_host.h>
 | |
| #include <linux/types.h>
 | |
| 
 | |
| typedef u64 kvm_pte_t;
 | |
| 
 | |
| /**
 | |
|  * struct kvm_pgtable - KVM page-table.
 | |
|  * @ia_bits:		Maximum input address size, in bits.
 | |
|  * @start_level:	Level at which the page-table walk starts.
 | |
|  * @pgd:		Pointer to the first top-level entry of the page-table.
 | |
|  * @mmu:		Stage-2 KVM MMU struct. Unused for stage-1 page-tables.
 | |
|  */
 | |
| struct kvm_pgtable {
 | |
| 	u32					ia_bits;
 | |
| 	u32					start_level;
 | |
| 	kvm_pte_t				*pgd;
 | |
| 
 | |
| 	/* Stage-2 only */
 | |
| 	struct kvm_s2_mmu			*mmu;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * enum kvm_pgtable_prot - Page-table permissions and attributes.
 | |
|  * @KVM_PGTABLE_PROT_X:		Execute permission.
 | |
|  * @KVM_PGTABLE_PROT_W:		Write permission.
 | |
|  * @KVM_PGTABLE_PROT_R:		Read permission.
 | |
|  * @KVM_PGTABLE_PROT_DEVICE:	Device attributes.
 | |
|  */
 | |
| enum kvm_pgtable_prot {
 | |
| 	KVM_PGTABLE_PROT_X			= BIT(0),
 | |
| 	KVM_PGTABLE_PROT_W			= BIT(1),
 | |
| 	KVM_PGTABLE_PROT_R			= BIT(2),
 | |
| 
 | |
| 	KVM_PGTABLE_PROT_DEVICE			= BIT(3),
 | |
| };
 | |
| 
 | |
| #define PAGE_HYP		(KVM_PGTABLE_PROT_R | KVM_PGTABLE_PROT_W)
 | |
| #define PAGE_HYP_EXEC		(KVM_PGTABLE_PROT_R | KVM_PGTABLE_PROT_X)
 | |
| #define PAGE_HYP_RO		(KVM_PGTABLE_PROT_R)
 | |
| #define PAGE_HYP_DEVICE		(PAGE_HYP | KVM_PGTABLE_PROT_DEVICE)
 | |
| 
 | |
| /**
 | |
|  * enum kvm_pgtable_walk_flags - Flags to control a depth-first page-table walk.
 | |
|  * @KVM_PGTABLE_WALK_LEAF:		Visit leaf entries, including invalid
 | |
|  *					entries.
 | |
|  * @KVM_PGTABLE_WALK_TABLE_PRE:		Visit table entries before their
 | |
|  *					children.
 | |
|  * @KVM_PGTABLE_WALK_TABLE_POST:	Visit table entries after their
 | |
|  *					children.
 | |
|  */
 | |
| enum kvm_pgtable_walk_flags {
 | |
| 	KVM_PGTABLE_WALK_LEAF			= BIT(0),
 | |
| 	KVM_PGTABLE_WALK_TABLE_PRE		= BIT(1),
 | |
| 	KVM_PGTABLE_WALK_TABLE_POST		= BIT(2),
 | |
| };
 | |
| 
 | |
| typedef int (*kvm_pgtable_visitor_fn_t)(u64 addr, u64 end, u32 level,
 | |
| 					kvm_pte_t *ptep,
 | |
| 					enum kvm_pgtable_walk_flags flag,
 | |
| 					void * const arg);
 | |
| 
 | |
| /**
 | |
|  * struct kvm_pgtable_walker - Hook into a page-table walk.
 | |
|  * @cb:		Callback function to invoke during the walk.
 | |
|  * @arg:	Argument passed to the callback function.
 | |
|  * @flags:	Bitwise-OR of flags to identify the entry types on which to
 | |
|  *		invoke the callback function.
 | |
|  */
 | |
| struct kvm_pgtable_walker {
 | |
| 	const kvm_pgtable_visitor_fn_t		cb;
 | |
| 	void * const				arg;
 | |
| 	const enum kvm_pgtable_walk_flags	flags;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_hyp_init() - Initialise a hypervisor stage-1 page-table.
 | |
|  * @pgt:	Uninitialised page-table structure to initialise.
 | |
|  * @va_bits:	Maximum virtual address bits.
 | |
|  *
 | |
|  * Return: 0 on success, negative error code on failure.
 | |
|  */
 | |
| int kvm_pgtable_hyp_init(struct kvm_pgtable *pgt, u32 va_bits);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_hyp_destroy() - Destroy an unused hypervisor stage-1 page-table.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_hyp_init().
 | |
|  *
 | |
|  * The page-table is assumed to be unreachable by any hardware walkers prior
 | |
|  * to freeing and therefore no TLB invalidation is performed.
 | |
|  */
 | |
| void kvm_pgtable_hyp_destroy(struct kvm_pgtable *pgt);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_hyp_map() - Install a mapping in a hypervisor stage-1 page-table.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_hyp_init().
 | |
|  * @addr:	Virtual address at which to place the mapping.
 | |
|  * @size:	Size of the mapping.
 | |
|  * @phys:	Physical address of the memory to map.
 | |
|  * @prot:	Permissions and attributes for the mapping.
 | |
|  *
 | |
|  * The offset of @addr within a page is ignored, @size is rounded-up to
 | |
|  * the next page boundary and @phys is rounded-down to the previous page
 | |
|  * boundary.
 | |
|  *
 | |
|  * If device attributes are not explicitly requested in @prot, then the
 | |
|  * mapping will be normal, cacheable. Attempts to install a new mapping
 | |
|  * for a virtual address that is already mapped will be rejected with an
 | |
|  * error and a WARN().
 | |
|  *
 | |
|  * Return: 0 on success, negative error code on failure.
 | |
|  */
 | |
| int kvm_pgtable_hyp_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys,
 | |
| 			enum kvm_pgtable_prot prot);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_stage2_init() - Initialise a guest stage-2 page-table.
 | |
|  * @pgt:	Uninitialised page-table structure to initialise.
 | |
|  * @kvm:	KVM structure representing the guest virtual machine.
 | |
|  *
 | |
|  * Return: 0 on success, negative error code on failure.
 | |
|  */
 | |
| int kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm *kvm);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_stage2_destroy() - Destroy an unused guest stage-2 page-table.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_stage2_init().
 | |
|  *
 | |
|  * The page-table is assumed to be unreachable by any hardware walkers prior
 | |
|  * to freeing and therefore no TLB invalidation is performed.
 | |
|  */
 | |
| void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_stage2_map() - Install a mapping in a guest stage-2 page-table.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_stage2_init().
 | |
|  * @addr:	Intermediate physical address at which to place the mapping.
 | |
|  * @size:	Size of the mapping.
 | |
|  * @phys:	Physical address of the memory to map.
 | |
|  * @prot:	Permissions and attributes for the mapping.
 | |
|  * @mc:		Cache of pre-allocated GFP_PGTABLE_USER memory from which to
 | |
|  *		allocate page-table pages.
 | |
|  *
 | |
|  * The offset of @addr within a page is ignored, @size is rounded-up to
 | |
|  * the next page boundary and @phys is rounded-down to the previous page
 | |
|  * boundary.
 | |
|  *
 | |
|  * If device attributes are not explicitly requested in @prot, then the
 | |
|  * mapping will be normal, cacheable.
 | |
|  *
 | |
|  * Note that this function will both coalesce existing table entries and split
 | |
|  * existing block mappings, relying on page-faults to fault back areas outside
 | |
|  * of the new mapping lazily.
 | |
|  *
 | |
|  * Return: 0 on success, negative error code on failure.
 | |
|  */
 | |
| int kvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
 | |
| 			   u64 phys, enum kvm_pgtable_prot prot,
 | |
| 			   struct kvm_mmu_memory_cache *mc);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_stage2_unmap() - Remove a mapping from a guest stage-2 page-table.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_stage2_init().
 | |
|  * @addr:	Intermediate physical address from which to remove the mapping.
 | |
|  * @size:	Size of the mapping.
 | |
|  *
 | |
|  * The offset of @addr within a page is ignored and @size is rounded-up to
 | |
|  * the next page boundary.
 | |
|  *
 | |
|  * TLB invalidation is performed for each page-table entry cleared during the
 | |
|  * unmapping operation and the reference count for the page-table page
 | |
|  * containing the cleared entry is decremented, with unreferenced pages being
 | |
|  * freed. Unmapping a cacheable page will ensure that it is clean to the PoC if
 | |
|  * FWB is not supported by the CPU.
 | |
|  *
 | |
|  * Return: 0 on success, negative error code on failure.
 | |
|  */
 | |
| int kvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_stage2_wrprotect() - Write-protect guest stage-2 address range
 | |
|  *                                  without TLB invalidation.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_stage2_init().
 | |
|  * @addr:	Intermediate physical address from which to write-protect,
 | |
|  * @size:	Size of the range.
 | |
|  *
 | |
|  * The offset of @addr within a page is ignored and @size is rounded-up to
 | |
|  * the next page boundary.
 | |
|  *
 | |
|  * Note that it is the caller's responsibility to invalidate the TLB after
 | |
|  * calling this function to ensure that the updated permissions are visible
 | |
|  * to the CPUs.
 | |
|  *
 | |
|  * Return: 0 on success, negative error code on failure.
 | |
|  */
 | |
| int kvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_stage2_mkyoung() - Set the access flag in a page-table entry.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_stage2_init().
 | |
|  * @addr:	Intermediate physical address to identify the page-table entry.
 | |
|  *
 | |
|  * The offset of @addr within a page is ignored.
 | |
|  *
 | |
|  * If there is a valid, leaf page-table entry used to translate @addr, then
 | |
|  * set the access flag in that entry.
 | |
|  *
 | |
|  * Return: The old page-table entry prior to setting the flag, 0 on failure.
 | |
|  */
 | |
| kvm_pte_t kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_stage2_mkold() - Clear the access flag in a page-table entry.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_stage2_init().
 | |
|  * @addr:	Intermediate physical address to identify the page-table entry.
 | |
|  *
 | |
|  * The offset of @addr within a page is ignored.
 | |
|  *
 | |
|  * If there is a valid, leaf page-table entry used to translate @addr, then
 | |
|  * clear the access flag in that entry.
 | |
|  *
 | |
|  * Note that it is the caller's responsibility to invalidate the TLB after
 | |
|  * calling this function to ensure that the updated permissions are visible
 | |
|  * to the CPUs.
 | |
|  *
 | |
|  * Return: The old page-table entry prior to clearing the flag, 0 on failure.
 | |
|  */
 | |
| kvm_pte_t kvm_pgtable_stage2_mkold(struct kvm_pgtable *pgt, u64 addr);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_stage2_is_young() - Test whether a page-table entry has the
 | |
|  *				   access flag set.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_stage2_init().
 | |
|  * @addr:	Intermediate physical address to identify the page-table entry.
 | |
|  *
 | |
|  * The offset of @addr within a page is ignored.
 | |
|  *
 | |
|  * Return: True if the page-table entry has the access flag set, false otherwise.
 | |
|  */
 | |
| bool kvm_pgtable_stage2_is_young(struct kvm_pgtable *pgt, u64 addr);
 | |
| 
 | |
| /**
 | |
|  * kvm_pgtable_walk() - Walk a page-table.
 | |
|  * @pgt:	Page-table structure initialised by kvm_pgtable_*_init().
 | |
|  * @addr:	Input address for the start of the walk.
 | |
|  * @size:	Size of the range to walk.
 | |
|  * @walker:	Walker callback description.
 | |
|  *
 | |
|  * The offset of @addr within a page is ignored and @size is rounded-up to
 | |
|  * the next page boundary.
 | |
|  *
 | |
|  * The walker will walk the page-table entries corresponding to the input
 | |
|  * address range specified, visiting entries according to the walker flags.
 | |
|  * Invalid entries are treated as leaf entries. Leaf entries are reloaded
 | |
|  * after invoking the walker callback, allowing the walker to descend into
 | |
|  * a newly installed table.
 | |
|  *
 | |
|  * Returning a negative error code from the walker callback function will
 | |
|  * terminate the walk immediately with the same error code.
 | |
|  *
 | |
|  * Return: 0 on success, negative error code on failure.
 | |
|  */
 | |
| int kvm_pgtable_walk(struct kvm_pgtable *pgt, u64 addr, u64 size,
 | |
| 		     struct kvm_pgtable_walker *walker);
 | |
| 
 | |
| #endif	/* __ARM64_KVM_PGTABLE_H__ */
 |