mirror of
https://github.com/torvalds/linux.git
synced 2026-09-26 10:02:02 +02:00
KVM: s390: pv: Use VM_SPARSE area for guest variable storage area
The guest variable storage area is allocated with vmalloc and then donated to the ultravisor. Any kernel access to that area will result in a secure storage access exception (aka fault). This is a problem if such a memory area is read via /proc/kcore. This causes an exception via vread_iter() and results in an unexpected short read. Avoid this by allocating a custom VM_SPARSE area. If such an area is read, vread_iter() returns zeroes for the entire area. Note that the function which frees the area does not update ptes. This is intentional to allow for deferred / lazy pte updates and TLB flushing like the generic vfree() code is doing that. See vunmap_pte_range(). This assumes that s390 will gain full support for lazy_mmu_mode_enable() and lazy_mmu_mode_disable() in the future, since as of now the used ptep_get_and_clear() in vunmap_pte_range() does indeed invalidate and flush every single pte entry, but only for s390. Tested-by: Christian Borntraeger <borntraeger@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Reviewed-by: Christian Borntraeger <borntraeger@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
parent
ffa796cc1f
commit
aab0734094
|
|
@ -635,6 +635,8 @@ int s390_wiggle_split_folio(struct mm_struct *mm, struct folio *folio);
|
|||
int __make_folio_secure(struct folio *folio, struct uv_cb_header *uvcb);
|
||||
int uv_convert_from_secure(unsigned long paddr);
|
||||
int uv_convert_from_secure_folio(struct folio *folio);
|
||||
void *uv_alloc_stor_var(unsigned long size);
|
||||
void uv_free_stor_var(void *stor_var);
|
||||
|
||||
void setup_uv(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/swap.h>
|
||||
#include <linux/pagewalk.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/facility.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/uv.h>
|
||||
|
|
@ -209,6 +210,70 @@ int uv_convert_from_secure_pte(pte_t pte)
|
|||
return uv_convert_from_secure_folio(pfn_folio(pte_pfn(pte)));
|
||||
}
|
||||
|
||||
static int uv_free_range_cb(pte_t *ptep, unsigned long addr, void *data)
|
||||
{
|
||||
pte_t pte = ptep_get(ptep);
|
||||
|
||||
if (!pte_present(pte))
|
||||
return 0;
|
||||
/*
|
||||
* Note: do not update the pte here, since there is no code which
|
||||
* accesses the memory range, besides bugs. The invalidation of ptes
|
||||
* and TLB flushing is deferred like for regular vfree() calls.
|
||||
*/
|
||||
__free_page(pte_page(pte));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uv_free_stor_var(void *stor_var)
|
||||
{
|
||||
unsigned long addr, size;
|
||||
struct vm_struct *area;
|
||||
|
||||
if (!stor_var)
|
||||
return;
|
||||
area = find_vm_area(stor_var);
|
||||
if (WARN_ON_ONCE(!area || !(area->flags & VM_SPARSE)))
|
||||
return;
|
||||
size = get_vm_area_size(area);
|
||||
addr = (unsigned long)area->addr;
|
||||
apply_to_existing_page_range(&init_mm, addr, size, uv_free_range_cb, NULL);
|
||||
free_vm_area(area);
|
||||
}
|
||||
EXPORT_SYMBOL_FOR_MODULES(uv_free_stor_var, "kvm");
|
||||
|
||||
static int uv_alloc_range_cb(pte_t *ptep, unsigned long addr, void *data)
|
||||
{
|
||||
struct page *page;
|
||||
pte_t pte;
|
||||
|
||||
page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
pte = __pte(page_to_phys(page) | pgprot_val(PAGE_KERNEL));
|
||||
set_pte(ptep, pte);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *uv_alloc_stor_var(unsigned long size)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
unsigned long addr;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
area = get_vm_area(size, VM_SPARSE);
|
||||
if (!area)
|
||||
return NULL;
|
||||
addr = (unsigned long)area->addr;
|
||||
if (apply_to_page_range(&init_mm, addr, size, uv_alloc_range_cb, NULL))
|
||||
goto out;
|
||||
return area->addr;
|
||||
out:
|
||||
uv_free_stor_var(area->addr);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_FOR_MODULES(uv_alloc_stor_var, "kvm");
|
||||
|
||||
/*
|
||||
* Calculate the expected ref_count for a folio that would otherwise have no
|
||||
* further pins. This was cribbed from similar functions in other places in
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
|
|||
/* only free resources when the destroy was successful */
|
||||
static void kvm_s390_pv_dealloc_vm(struct kvm *kvm)
|
||||
{
|
||||
vfree(kvm->arch.pv.stor_var);
|
||||
uv_free_stor_var(kvm->arch.pv.stor_var);
|
||||
free_pages(kvm->arch.pv.stor_base,
|
||||
get_order(uv_info.guest_base_stor_len));
|
||||
kvm_s390_clear_pv_state(kvm);
|
||||
|
|
@ -369,7 +369,7 @@ static int kvm_s390_pv_alloc_vm(struct kvm *kvm)
|
|||
/* Allocate variable storage */
|
||||
vlen = ALIGN(virt * ((npages * PAGE_SIZE) / HPAGE_SIZE), PAGE_SIZE);
|
||||
vlen += uv_info.guest_virt_base_stor_len;
|
||||
kvm->arch.pv.stor_var = vzalloc(vlen);
|
||||
kvm->arch.pv.stor_var = uv_alloc_stor_var(vlen);
|
||||
if (!kvm->arch.pv.stor_var)
|
||||
goto out_err;
|
||||
return 0;
|
||||
|
|
@ -414,7 +414,7 @@ static int kvm_s390_pv_dispose_one_leftover(struct kvm *kvm,
|
|||
*/
|
||||
free_pages(leftover->stor_base, get_order(uv_info.guest_base_stor_len));
|
||||
free_pages(leftover->old_gmap_table, CRST_ALLOC_ORDER);
|
||||
vfree(leftover->stor_var);
|
||||
uv_free_stor_var(leftover->stor_var);
|
||||
done_fast:
|
||||
atomic_dec(&kvm->mm->context.protected_count);
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user