KVM: introduce readonly memslot
Subsystems (4)
v4 cover KVM: introduce readonly memslot
In current code, if we map a readonly memory space from host to guest and the page is not currently mapped in the host, we will get a fault-pfn and async is not allowed, then the vm will crash.
As Avi's suggestion, We introduce readonly memory region to map ROM/ROMD to the guest, read access is happy for readonly memslot, write access on readonly memslot will cause KVM_EXIT_MMIO exit.
The test case attached is used to test this feather.
Changlog:
- fix memslot flag check
- fix caching the mmio info into spte which is caused by write on readonly memslot
- extend mmio-exit info to indicate whether the mmio-exit is caused by readonly fault
PATCH v4 1/10 KVM: fix missing check for memslot flags
Check flags when memslot is registered from userspace as Avi's suggestion
--- virt/kvm/kvm_main.c | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
virt/kvm/kvm_main.c +12 -0
@@ -689,6 +689,14 @@ void update_memslots(struct kvm_memslots *slots, struct kvm_memory_slot *new) slots->generation++; } +static int check_memory_region_flags (struct kvm_userspace_memory_region *mem) +{ +if (mem->flags & ~KVM_MEM_LOG_DIRTY_PAGES ) +return -EINVAL ; + +return 0 ; +} +/* * Allocate some memory and give it an address in the guest physical address * space.
@@ -709,6 +717,10 @@ int __kvm_set_memory_region(struct kvm *kvm,struct kvm_memory_slot old, new;struct kvm_memslots *slots, *old_memslots; + r =check_memory_region_flags (mem); +if (r) + goto out; + r = -EINVAL ;/* General sanity checks */ if (mem->memory_size & (PAGE_SIZE -1 ))
Signature · 1 line
1.7.7.6
PATCH v4 2/10 KVM: hide KVM_MEMSLOT_INVALID from userspace
In reply to Xiao GuangrongQuote Avi's comment:
| KVM_MEMSLOT_INVALID is actually an internal symbol, not used by | userspace. Please move it to kvm_host.h.
Also, move KVM_MEMSLOT_INVALID to the highest bit
--- include/linux/kvm.h | 1 - include/linux/kvm_host.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletions(-)
include/linux/kvm.h +0 -1
@@ -103,7 +103,6 @@ struct kvm_userspace_memory_region {/* for kvm_memory_region::flags */ #define KVM_MEM_LOG_DIRTY_PAGES 1UL -#define KVM_MEMSLOT_INVALID (1UL << 1)/* for KVM_IRQ_LINE */ struct kvm_irq_level {
include/linux/kvm_host.h +2 -0
@@ -34,6 +34,8 @@#define KVM_MMIO_SIZE 8#endif +#define KVM_MEMSLOT_INVALID (1UL << 31) +/* * If we support unaligned MMIO, at most one fragment will be split into two: */
Signature · 1 line
1.7.7.6
PATCH v4 3/10 KVM: introduce gfn_to_pfn_memslot_atomic
In reply to Xiao GuangrongIt can instead of hva_to_pfn_atomic
--- arch/x86/kvm/mmu.c | 5 +---- include/linux/kvm_host.h | 3 ++- virt/kvm/kvm_main.c | 14 ++++++++------ 3 files changed, 11 insertions(+), 11 deletions(-)
arch/x86/kvm/mmu.c +1 -4
@@ -2480,15 +2480,12 @@ static pfn_t pte_prefetch_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn, bool no_dirty_log) {struct kvm_memory_slot *slot; -unsigned long hva; slot =gfn_to_memslot_dirty_bitmap (vcpu, gfn, no_dirty_log);if (!slot)return get_fault_pfn (); - hva =gfn_to_hva_memslot (slot, gfn); - -return hva_to_pfn_atomic (hva); +return gfn_to_pfn_memslot_atomic (slot, gfn); } static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu,
include/linux/kvm_host.h +2 -1
@@ -420,7 +420,6 @@ void kvm_release_page_dirty(struct page *page);void kvm_set_page_dirty (struct page *page);void kvm_set_page_accessed (struct page *page); -pfn_t hva_to_pfn_atomic (unsigned long addr);pfn_t gfn_to_pfn_atomic (struct kvm *kvm,gfn_t gfn);pfn_t gfn_to_pfn_async (struct kvm *kvm,gfn_t gfn,bool *async,bool write_fault,bool *writable);
@@ -428,6 +427,8 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn);pfn_t gfn_to_pfn_prot (struct kvm *kvm,gfn_t gfn,bool write_fault,bool *writable);pfn_t gfn_to_pfn_memslot (struct kvm_memory_slot *slot,gfn_t gfn); +pfn_t gfn_to_pfn_memslot_atomic (struct kvm_memory_slot *slot,gfn_t gfn); +void kvm_release_pfn_dirty (pfn_t );void kvm_release_pfn_clean (pfn_t pfn);void kvm_set_pfn_dirty (pfn_t pfn);
virt/kvm/kvm_main.c +8 -6
@@ -1156,12 +1156,6 @@ static pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async,return pfn; } -pfn_t hva_to_pfn_atomic (unsigned long addr) -{ -return hva_to_pfn (addr, true,NULL , true,NULL ); -} -EXPORT_SYMBOL_GPL (hva_to_pfn_atomic); -static pfn_t __gfn_to_pfn (struct kvm *kvm,gfn_t gfn,bool atomic,bool *async,bool write_fault,bool *writable) {
@@ -1211,6 +1205,14 @@ pfn_t gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn)return hva_to_pfn (addr, false,NULL , true,NULL ); } +pfn_t gfn_to_pfn_memslot_atomic (struct kvm_memory_slot *slot,gfn_t gfn) +{ +unsigned long addr =gfn_to_hva_memslot (slot, gfn); + +return hva_to_pfn (addr, true,NULL , true,NULL ); +} +EXPORT_SYMBOL_GPL (gfn_to_pfn_memslot_atomic); +int gfn_to_page_many_atomic (struct kvm *kvm,gfn_t gfn,struct page **pages,int nr_pages) {
Signature · 1 line
1.7.7.6
PATCH v4 4/10 KVM: introduce gfn_to_hva_read/kvm_read_hva/kvm_read_hva_atomic
In reply to Xiao GuangrongThis set of functions is only used to read data from host space, in the later patch, we will only get a readonly hva in gfn_to_hva_read, and the function name is a good hint to let gfn_to_hva_read to pair with kvm_read_hva()/kvm_read_hva_atomic()
--- virt/kvm/kvm_main.c | 29 +++++++++++++++++++++++++---- 1 files changed, 25 insertions(+), 4 deletions(-)
virt/kvm/kvm_main.c +25 -4
@@ -1048,6 +1048,27 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn) }EXPORT_SYMBOL_GPL (gfn_to_hva); +/* +* The hva returned by this function is only allowed to be read. +* It should pair with kvm_read_hva() or kvm_read_hva_atomic(). +*/ +static unsigned long gfn_to_hva_read (struct kvm *kvm,gfn_t gfn) +{ +return gfn_to_hva_many (gfn_to_memslot (kvm, gfn), gfn,NULL ); +} + +static int kvm_read_hva (void *data,void __user *hva,int len) +{ +return __copy_from_user (data, hva, len); + +} + +static int kvm_read_hva_atomic (void *data,void __user *hva,int len) +{ +return __copy_from_user_inatomic (data, hva, len); + +} +pfn_t get_fault_pfn (void ) {get_page (fault_page);
@@ -1316,10 +1337,10 @@ int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset,int r;unsigned long addr; - addr =gfn_to_hva (kvm, gfn); + addr =gfn_to_hva_read (kvm, gfn);if (kvm_is_error_hva (addr))return -EFAULT ; - r =__copy_from_user (data, (void __user *)addr + offset, len); + r =kvm_read_hva (data, (void __user *)addr + offset, len);if (r)return -EFAULT ;return 0 ;
@@ -1354,11 +1375,11 @@ int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data,gfn_t gfn = gpa >>PAGE_SHIFT ;int offset =offset_in_page (gpa); - addr =gfn_to_hva (kvm, gfn); + addr =gfn_to_hva_read (kvm, gfn);if (kvm_is_error_hva (addr))return -EFAULT ;pagefault_disable (); - r =__copy_from_user_inatomic (data, (void __user *)addr + offset, len); + r =kvm_read_hva_atomic (data, (void __user *)addr + offset, len);pagefault_enable ();if (r)return -EFAULT ;
Signature · 1 line
1.7.7.6
PATCH v4 5/10 KVM: reorganize hva_to_pfn
In reply to Xiao GuangrongWe do too many things in hva_to_pfn, this patch reorganize the code, let it be better readable
--- virt/kvm/kvm_main.c | 161 +++++++++++++++++++++++++++++++-------------------- 1 files changed, 99 insertions(+), 62 deletions(-)
virt/kvm/kvm_main.c +99 -62
@@ -1096,84 +1096,121 @@ static inline int check_user_page_hwpoison(unsigned long addr)return rc == -EHWPOISON ; } -static pfn_t hva_to_pfn (unsigned long addr,bool atomic,bool *async, -bool write_fault,bool *writable) +/* +* The atomic path to get the writable pfn which will be stored in @pfn, +* true indicates success, otherwise false is returned. +*/ +static bool hva_to_pfn_fast (unsigned long addr,bool atomic,bool *async, +bool write_fault,bool *writable,pfn_t *pfn) {struct page *page[1 ]; -int npages =0 ; -pfn_t pfn; +int npages; -/* we can do it either atomically or asynchronously, not both */ -BUG_ON (atomic && async); +if (!(async || atomic)) +return false; -BUG_ON (!write_fault && !writable); + npages =__get_user_pages_fast (addr,1 ,1 , page); +if (npages ==1 ) { + *pfn =page_to_pfn (page[0 ]); + +if (writable) + *writable = true; +return true; + } + +return false; +} + +/* +* The slow path to get the pfn of the specified host virtual address, +* 1 indicates success, -errno is returned if error is detected. +*/ +static int hva_to_pfn_slow (unsigned long addr,bool *async,bool write_fault, +bool *writable,pfn_t *pfn) +{ +struct page *page[1 ]; +int npages =0 ; + +might_sleep ();if (writable) - *writable = true; + *writable = write_fault; -if (atomic || async) - npages =__get_user_pages_fast (addr,1 ,1 , page); +if (async) { +down_read (¤t->mm->mmap_sem); + npages =get_user_page_nowait (current, current->mm, + addr, write_fault, page); +up_read (¤t->mm->mmap_sem); + }else + npages =get_user_pages_fast (addr,1 , write_fault, + page); -if (unlikely (npages !=1 ) && !atomic) { -might_sleep (); +if (npages !=1 ) +return npages; -if (writable) - *writable = write_fault; - -if (async) { -down_read (¤t->mm->mmap_sem); - npages =get_user_page_nowait (current, current->mm, - addr, write_fault, page); -up_read (¤t->mm->mmap_sem); - }else - npages =get_user_pages_fast (addr,1 , write_fault, - page); - -/* map read fault as writable if possible */ -if (unlikely (!write_fault) && npages ==1 ) { -struct page *wpage[1 ]; - - npages =__get_user_pages_fast (addr,1 ,1 , wpage); -if (npages ==1 ) { - *writable = true; -put_page (page[0 ]); - page[0 ] = wpage[0 ]; - } - npages =1 ; +/* map read fault as writable if possible */ +if (unlikely (!write_fault)) { +struct page *wpage[1 ]; + + npages =__get_user_pages_fast (addr,1 ,1 , wpage); +if (npages ==1 ) { + *writable = true; +put_page (page[0 ]); + page[0 ] = wpage[0 ]; } + + npages =1 ; } + *pfn =page_to_pfn (page[0 ]); +return npages; +} -if (unlikely (npages !=1 )) { -struct vm_area_struct *vma; +static pfn_t hva_to_pfn (unsigned long addr,bool atomic,bool *async, +bool write_fault,bool *writable) +{ +struct vm_area_struct *vma; +pfn_t pfn =0 ; +int npages; -if (atomic) -return get_fault_pfn (); +/* we can do it either atomically or asynchronously, not both */ +BUG_ON (atomic && async); -down_read (¤t->mm->mmap_sem); -if (npages == -EHWPOISON || - (!async &&check_user_page_hwpoison (addr))) { -up_read (¤t->mm->mmap_sem); -get_page (hwpoison_page); -return page_to_pfn (hwpoison_page); - } +BUG_ON (!write_fault && !writable); - vma =find_vma_intersection (current->mm, addr, addr+1 ); - -if (vma ==NULL ) - pfn =get_fault_pfn (); -else if ((vma->vm_flags &VM_PFNMAP )) { - pfn = ((addr - vma->vm_start) >>PAGE_SHIFT ) + - vma->vm_pgoff; -BUG_ON (!kvm_is_mmio_pfn (pfn)); - }else { -if (async && (vma->vm_flags &VM_WRITE )) - *async = true; - pfn =get_fault_pfn (); - } -up_read (¤t->mm->mmap_sem); - }else - pfn =page_to_pfn (page[0 ]); +if (hva_to_pfn_fast (addr, atomic, async, write_fault, writable, &pfn)) +return pfn; +if (atomic) +return get_fault_pfn (); + + npages =hva_to_pfn_slow (addr, async, write_fault, writable, &pfn); +if (npages ==1 ) +return pfn; + +down_read (¤t->mm->mmap_sem); +if (npages == -EHWPOISON || + (!async &&check_user_page_hwpoison (addr))) { +get_page (hwpoison_page); + pfn =page_to_pfn (hwpoison_page); + goto exit; + } + + vma =find_vma_intersection (current->mm, addr, addr +1 ); + +if (vma ==NULL ) + pfn =get_fault_pfn (); +else if ((vma->vm_flags &VM_PFNMAP )) { + pfn = ((addr - vma->vm_start) >>PAGE_SHIFT ) + + vma->vm_pgoff; +BUG_ON (!kvm_is_mmio_pfn (pfn)); + }else { +if (async && (vma->vm_flags &VM_WRITE )) + *async = true; + pfn =get_fault_pfn (); + } + +exit: +up_read (¤t->mm->mmap_sem);return pfn; }
Signature · 1 line
1.7.7.6
PATCH v4 6/10 KVM: use 'writable' as a hint to map writable pfn
In reply to Xiao GuangrongIn current code, we always map writable pfn for the read-fault, in order to support readonly memslot, we map writable pfn only if 'writable' is not NULL
--- virt/kvm/kvm_main.c | 24 +++++++++++++++++++++++- 1 files changed, 23 insertions(+), 1 deletions(-)
virt/kvm/kvm_main.c +23 -1
@@ -1109,6 +1109,14 @@ static bool hva_to_pfn_fast(unsigned long addr, bool atomic, bool *async,if (!(async || atomic))return false; +/* +* Fast pin a writable pfn only if it is a write fault request +* or the caller allows to map a writable pfn for a read fault +* request. +*/ +if (!(write_fault || writable)) +return false; + npages =__get_user_pages_fast (addr,1 ,1 , page);if (npages ==1 ) { *pfn =page_to_pfn (page[0 ]);
@@ -1149,7 +1157,7 @@ static int hva_to_pfn_slow(unsigned long addr, bool *async, bool write_fault,return npages;/* map read fault as writable if possible */ -if (unlikely (!write_fault)) { +if (unlikely (!write_fault) && writable) {struct page *wpage[1 ]; npages =__get_user_pages_fast (addr,1 ,1 , wpage);
@@ -1165,6 +1173,20 @@ static int hva_to_pfn_slow(unsigned long addr, bool *async, bool write_fault,return npages; } +/* +* Pin guest page in memory and return its pfn. +* @addr: host virtual address which maps memory to the guest +* @atomic: whether this function can sleep +* @async: whether this function need to wait IO complete if the +* host page is not in the memory +* @write_fault: whether we should get a writable host page +* @writable: whether it allows to map a writable host page for !@write_fault +* +* The function will map a writable host page for these two cases: +* 1): @write_fault = true +* 2): @write_fault = false && @writable, @writable will tell the caller +* whether the mapping is writable. +*/ static pfn_t hva_to_pfn (unsigned long addr,bool atomic,bool *async,bool write_fault,bool *writable) {
Signature · 1 line
1.7.7.6
PATCH v4 7/10 KVM: introduce readonly_fault_pfn
In reply to Xiao GuangrongIntroduce readonly_fault_pfn, in the later patch, it indicates failure when we try to get a writable pfn from the readonly memslot
--- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 92 +++++++++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 37 deletions(-)
include/linux/kvm_host.h +1 -0
@@ -385,6 +385,7 @@ extern struct page *bad_page;int is_error_page (struct page *page);int is_error_pfn (pfn_t pfn);int is_hwpoison_pfn (pfn_t pfn); +int is_readonly_fault_pfn (pfn_t pfn);int is_noslot_pfn (pfn_t pfn);int is_invalid_pfn (pfn_t pfn);int kvm_is_error_hva (unsigned long addr);
virt/kvm/kvm_main.c +55 -37
155 lines not on this page · raw message
Signature · 1 line
1.7.7.6
Re: [PATCH 07/10] KVM: introduce readonly_fault_pfn
Quoted 8 lines · Xiao Guangrong
Introduce
readonly_fault_pfn, in the later patch, it indicates failure when we try to get a writable pfn from the readonly memslotvirt/kvm/kvm_main.c +1
-0+inline int kvm_is_mmio_pfn (pfn_t pfn) {if (pfn_valid (pfn)) {
virt/kvm/kvm_main.c EXPORT_SYMBOL_GPL(kvm_disable_largepages); · line 956
Quoted 6 lines
virt/kvm/kvm_main.c +2
-1@@ -949,13 +952,15 @@ EXPORT_SYMBOL_GPL(kvm_disable_largepages);int is_error_page (struct page *page) { -return page == bad_page || page == hwpoison_page || page == fault_page; +return page == bad_page || page == hwpoison_page || page == fault_page + || page == readonly_fault_page;
All those checks are slow, and get_page(fault_page) etc. isn't very scalable.
We should move to ERR_PTR() etc. We could use ENOENT, EHWPOISON, EFAULT, and EROFS for the above, or maybe there are better matches.
virt/kvm/kvm_main.c EXPORT_SYMBOL_GPL(kvm_disable_largepages); · line 964
Quoted 9 lines
virt/kvm/kvm_main.c +2
-1}EXPORT_SYMBOL_GPL (is_error_page);int is_error_pfn (pfn_t pfn) { -return pfn == bad_pfn || pfn == hwpoison_pfn || pfn == fault_pfn; +return pfn == bad_pfn || pfn == hwpoison_pfn || pfn == fault_pfn + || pfn == readonly_fault_pfn; }
And a similar change here.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 07/10] KVM: introduce readonly_fault_pfn
Quoted 19 lines · Avi Kivity
Quoted 8 lines · Xiao Guangrong
Introduce
readonly_fault_pfn, in the later patch, it indicates failure when we try to get a writable pfn from the readonly memslotvirt/kvm/kvm_main.c +1
-0+inline int kvm_is_mmio_pfn (pfn_t pfn) {if (pfn_valid (pfn)) {virt/kvm/kvm_main.c EXPORT_SYMBOL_GPL(kvm_disable_largepages); · line 956
Quoted 6 lines
virt/kvm/kvm_main.c +2
-1@@ -949,13 +952,15 @@ EXPORT_SYMBOL_GPL(kvm_disable_largepages);int is_error_page (struct page *page) { -return page == bad_page || page == hwpoison_page || page == fault_page; +return page == bad_page || page == hwpoison_page || page == fault_page + || page == readonly_fault_page;All those checks are slow, and
get_page(fault_page)etc. isn't very scalable.
Quoted 3 lines
We should move to
ERR_PTR()etc. We could use ENOENT, EHWPOISON, EFAULT, and EROFS for the above, or maybe there are better matches.
Good point. I will do it in the next version, thank you, Avi!
PATCH v4 8/10 KVM: introduce readonly_bad_hva
In reply to Xiao GuangrongIn the later patch, it indicates failure when we try to get a writable hva from the readonly slot
--- virt/kvm/kvm_main.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-)
virt/kvm/kvm_main.c +11 -1
@@ -994,9 +994,19 @@ static inline unsigned long bad_hva(void)return PAGE_OFFSET ; } +static inline unsigned long readonly_bad_hva (void ) +{ +return PAGE_OFFSET +PAGE_SIZE ; +} + +static int kvm_is_readonly_bad_hva (unsigned long addr) +{ +return addr ==readonly_bad_hva (); +} +int kvm_is_error_hva (unsigned long addr) { -return addr ==bad_hva (); +return addr ==bad_hva () ||kvm_is_readonly_bad_hva (addr); }EXPORT_SYMBOL_GPL (kvm_is_error_hva);
Signature · 1 line
1.7.7.6
Re: [PATCH 08/10] KVM: introduce readonly_bad_hva
Quoted 21 lines · Xiao Guangrong
In the later patch, it indicates failure when we try to get a writable hva from the readonly slot
Signed-off-by Xiao Guangrong <[email protected]>--- virt/kvm/kvm_main.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-)virt/kvm/kvm_main.c +5
-0@@ -994,9 +994,19 @@ static inline unsigned long bad_hva(void)return PAGE_OFFSET ; } +static inline unsigned long readonly_bad_hva (void ) +{ +return PAGE_OFFSET +PAGE_SIZE ; +} +
virt/kvm/kvm_main.c static inline unsigned long bad_hva(void) · line 1010
Quoted 10 lines
virt/kvm/kvm_main.c +6
-1+static int kvm_is_readonly_bad_hva (unsigned long addr) +{ +return addr ==readonly_bad_hva (); +} +int kvm_is_error_hva (unsigned long addr) { -return addr ==bad_hva (); +return addr ==bad_hva () ||kvm_is_readonly_bad_hva (addr); }
addr >= PAGE_OFFSET. Or change it to use -E*.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 08/10] KVM: introduce readonly_bad_hva
Quoted 26 lines · Avi Kivity
Quoted 25 lines · Xiao Guangrong
In the later patch, it indicates failure when we try to get a writable hva from the readonly slot
Signed-off-by Xiao Guangrong <[email protected]>--- virt/kvm/kvm_main.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-)virt/kvm/kvm_main.c +9
-0@@ -994,9 +994,19 @@ static inline unsigned long bad_hva(void)return PAGE_OFFSET ; } +static inline unsigned long readonly_bad_hva (void ) +{ +return PAGE_OFFSET +PAGE_SIZE ; +} + +static int kvm_is_readonly_bad_hva (unsigned long addr) +{ +return addr ==readonly_bad_hva (); +}
Quoted 8 lines
virt/kvm/kvm_main.c static inline unsigned long bad_hva(void) · line 1010
Quoted 6 lines
virt/kvm/kvm_main.c +2
-1+int kvm_is_error_hva (unsigned long addr) { -return addr ==bad_hva (); +return addr ==bad_hva () ||kvm_is_readonly_bad_hva (addr); }addr >=
PAGE_OFFSET. Or change it to use -E*.
I prefer to the first one, addr >= PAGE_OFFSET, all virtual addresses between 0 and (~0ULL) are valid, Using PAGE_OFFSET is more readable.
[ is_error_pfn is suitable to use -err because the the range of physical
address is always limited, for example, 0 ~ 64G on x86.]
PATCH v4 9/10 KVM: introduce readonly memslot
In reply to Xiao GuangrongIn current code, if we map a readonly memory space from host to guest and the page is not currently mapped in the host, we will get a fault-pfn and async is not allowed, then the vm will crash
We introduce readonly memory region to map ROM/ROMD to the guest, read access is happy for readonly memslot, write access on readonly memslot will cause KVM_EXIT_MMIO exit
--- Documentation/virtual/kvm/api.txt | 10 +++- arch/x86/include/asm/kvm.h | 1 + arch/x86/kvm/mmu.c | 10 ++++ arch/x86/kvm/x86.c | 1 + include/linux/kvm.h | 6 ++- virt/kvm/kvm_main.c | 84 ++++++++++++++++++++++++++++-------- 6 files changed, 89 insertions(+), 23 deletions(-)
Documentation/virtual/kvm/api.txt +7 -3
@@ -857,7 +857,8 @@ struct kvm_userspace_memory_region { }; /* for kvm_memory_region::flags */ -#define KVM_MEM_LOG_DIRTY_PAGES 1UL +#define KVM_MEM_LOG_DIRTY_PAGES (1UL << 0) +#define KVM_MEM_READONLY (1UL << 1) This ioctl allows the user to create or modify a guest physical memory slot. When changing an existing slot, it may be moved in the guest
@@ -873,9 +874,12 @@ It is recommended that the lower 21 bits of guest_phys_addr and userspace_addr be identical. This allows large pages in the guest to be backed by large pages in the host. -The flags field supports just one flag, KVM_MEM_LOG_DIRTY_PAGES, which +The flags field supports two flag, KVM_MEM_LOG_DIRTY_PAGES, which instructs kvm to keep track of writes to memory within the slot. See -the KVM_GET_DIRTY_LOG ioctl. +the KVM_GET_DIRTY_LOG ioctl. Another flag is KVM_MEM_READONLY when the +KVM_CAP_READONLY_MEM capability, it indicates the guest memory is read-only, +that means, guest is only allowed to read it. Writes will be posted to +userspace as KVM_EXIT_MMIO exits. When the KVM_CAP_SYNC_MMU capability, changes in the backing of the memory region are automatically reflected into the guest. For example, an mmap()
arch/x86/include/asm/kvm.h +1 -0
@@ -25,6 +25,7 @@#define __KVM_HAVE_DEBUGREGS#define __KVM_HAVE_XSAVE#define __KVM_HAVE_XCRS +#define __KVM_HAVE_READONLY_MEM/* Architectural interrupt line count. */ #define KVM_NR_INTERRUPTS 256
arch/x86/kvm/mmu.c +10 -0
@@ -2618,6 +2618,16 @@ static void kvm_send_hwpoison_signal(unsigned long address, struct task_struct *static int kvm_handle_bad_page (struct kvm_vcpu *vcpu,gfn_t gfn,pfn_t pfn) {kvm_release_pfn_clean (pfn); + +/* +* Do not cache the mmio info caused by writing the readonly gfn +* into the spte otherwise read access on readonly gfn also can +* caused mmio page fault and treat it as mmio access. +* Return 1 to tell kvm to emulate it. +*/ +if (is_readonly_fault_pfn (pfn)) +return 1 ; +if (is_hwpoison_pfn (pfn)) {kvm_send_hwpoison_signal (gfn_to_hva (vcpu->kvm, gfn), current);return 0 ;
arch/x86/kvm/x86.c +1 -0
@@ -2153,6 +2153,7 @@ int kvm_dev_ioctl_check_extension(long ext)case KVM_CAP_GET_TSC_KHZ :case KVM_CAP_PCI_2_3 :case KVM_CAP_KVMCLOCK_CTRL : +case KVM_CAP_READONLY_MEM : r =1 ;break ;case KVM_CAP_COALESCED_MMIO :
include/linux/kvm.h +5 -1
@@ -102,7 +102,8 @@ struct kvm_userspace_memory_region { };/* for kvm_memory_region::flags */ -#define KVM_MEM_LOG_DIRTY_PAGES 1UL +#define KVM_MEM_LOG_DIRTY_PAGES (1UL << 0) +#define KVM_MEM_READONLY (1UL << 1)/* for KVM_IRQ_LINE */ struct kvm_irq_level {
@@ -617,6 +618,9 @@ struct kvm_ppc_smmu_info {#define KVM_CAP_PPC_GET_SMMU_INFO 78#define KVM_CAP_S390_COW 79#define KVM_CAP_PPC_ALLOC_HTAB 80 +#ifdef __KVM_HAVE_READONLY_MEM +#define KVM_CAP_READONLY_MEM 81 +#endif #ifdef KVM_CAP_IRQ_ROUTING
virt/kvm/kvm_main.c +65 -19
154 lines not on this page · raw message
Signature · 1 line
1.7.7.6
PATCH v4 10/10 KVM: indicate readonly access fault
In reply to Xiao GuangrongIntroduce write_readonly_mem in mmio-exit-info to indicate this exit is caused by write access on readonly memslot
--- arch/x86/kvm/x86.c | 12 ++++++++---- include/linux/kvm.h | 3 +++ include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 3 +++ 4 files changed, 15 insertions(+), 4 deletions(-)
arch/x86/kvm/x86.c +8 -4
@@ -3703,9 +3703,10 @@ int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, ret =kvm_write_guest (vcpu->kvm, gpa, val, bytes);if (ret <0 ) -return 0 ; +return ret; +kvm_mmu_pte_write (vcpu, gpa, val, bytes); -return 1 ; +return 0 ; }struct read_write_emulator_ops {
@@ -3735,7 +3736,7 @@ static int read_prepare(struct kvm_vcpu *vcpu, void *val, int bytes)static int read_emulate (struct kvm_vcpu *vcpu,gpa_t gpa,void *val,int bytes) { -return !kvm_read_guest (vcpu->kvm, gpa, val, bytes); +return kvm_read_guest (vcpu->kvm, gpa, val, bytes); } static int write_emulate(struct kvm_vcpu *vcpu, gpa_t gpa,
@@ -3797,7 +3798,8 @@ static int emulator_read_write_onepage(unsigned long addr, void *val,if (ret) goto mmio; -if (ops->read_write_emulate (vcpu, gpa, val, bytes)) + ret = ops->read_write_emulate (vcpu, gpa, val, bytes); +if (!ret)return X86EMUL_CONTINUE ; mmio:
@@ -3819,6 +3821,7 @@ mmio: frag->gpa = gpa; frag->data = val; frag->len = now; + frag->write_readonly_mem = (ret == -EPERM ); gpa += now; val += now;
@@ -3836,6 +3839,7 @@ static void set_mmio_exit_info(struct kvm_vcpu *vcpu, run->mmio.phys_addr = frag->gpa; run->mmio.len = frag->len; run->mmio.is_write = vcpu->mmio_is_write = write; + run->mmio.write_readonly_mem = frag->write_readonly_mem;if (write)memcpy (run->mmio.data, frag->data, frag->len);
include/linux/kvm.h +3 -0
@@ -222,6 +222,9 @@ struct kvm_run {__u8 data[8 ];__u32 len;__u8 is_write; +#ifdef __KVM_HAVE_READONLY_MEM +__u8 write_readonly_mem; +#endif } mmio;/* KVM_EXIT_HYPERCALL */ struct {
include/linux/kvm_host.h +1 -0
@@ -142,6 +142,7 @@ struct kvm_mmio_fragment {gpa_t gpa;void *data;unsigned len; +bool write_readonly_mem; };struct kvm_vcpu {
virt/kvm/kvm_main.c +3 -0
@@ -1527,6 +1527,9 @@ int kvm_write_guest_page(struct kvm *kvm, gfn_t gfn, const void *data,unsigned long addr; addr =gfn_to_hva (kvm, gfn); +if (kvm_is_readonly_bad_hva (addr)) +return -EPERM ; +if (kvm_is_error_hva (addr))return -EFAULT ; r =__copy_to_user ((void __user *)addr + offset, data, len);
Signature · 1 line
1.7.7.6