mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

In the past %pK was preferable to %p as it would not leak raw pointer
values into the kernel log.
Since commit ad67b74d24
("printk: hash addresses printed with %p")
the regular %p has been improved to avoid this issue.
Furthermore, restricted pointers ("%pK") were never meant to be used
through printk(). They can still unintentionally leak raw pointers or
acquire sleeping locks in atomic contexts.
Switch to the regular pointer formatting which is safer and
easier to reason about.
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Link: https://lore.kernel.org/r/20250718-restricted-pointers-virt-v1-1-12913fceaf52@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
368 lines
9.1 KiB
C
368 lines
9.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* ACRN: Memory mapping management
|
|
*
|
|
* Copyright (C) 2020 Intel Corporation. All rights reserved.
|
|
*
|
|
* Authors:
|
|
* Fei Li <lei1.li@intel.com>
|
|
* Shuo Liu <shuo.a.liu@intel.com>
|
|
*/
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include "acrn_drv.h"
|
|
|
|
static int modify_region(struct acrn_vm *vm, struct vm_memory_region_op *region)
|
|
{
|
|
struct vm_memory_region_batch *regions;
|
|
int ret;
|
|
|
|
regions = kzalloc(sizeof(*regions), GFP_KERNEL);
|
|
if (!regions)
|
|
return -ENOMEM;
|
|
|
|
regions->vmid = vm->vmid;
|
|
regions->regions_num = 1;
|
|
regions->regions_gpa = virt_to_phys(region);
|
|
|
|
ret = hcall_set_memory_regions(virt_to_phys(regions));
|
|
if (ret < 0)
|
|
dev_dbg(acrn_dev.this_device,
|
|
"Failed to set memory region for VM[%u]!\n", vm->vmid);
|
|
|
|
kfree(regions);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* acrn_mm_region_add() - Set up the EPT mapping of a memory region.
|
|
* @vm: User VM.
|
|
* @user_gpa: A GPA of User VM.
|
|
* @service_gpa: A GPA of Service VM.
|
|
* @size: Size of the region.
|
|
* @mem_type: Combination of ACRN_MEM_TYPE_*.
|
|
* @mem_access_right: Combination of ACRN_MEM_ACCESS_*.
|
|
*
|
|
* Return: 0 on success, <0 on error.
|
|
*/
|
|
int acrn_mm_region_add(struct acrn_vm *vm, u64 user_gpa, u64 service_gpa,
|
|
u64 size, u32 mem_type, u32 mem_access_right)
|
|
{
|
|
struct vm_memory_region_op *region;
|
|
int ret = 0;
|
|
|
|
region = kzalloc(sizeof(*region), GFP_KERNEL);
|
|
if (!region)
|
|
return -ENOMEM;
|
|
|
|
region->type = ACRN_MEM_REGION_ADD;
|
|
region->user_vm_pa = user_gpa;
|
|
region->service_vm_pa = service_gpa;
|
|
region->size = size;
|
|
region->attr = ((mem_type & ACRN_MEM_TYPE_MASK) |
|
|
(mem_access_right & ACRN_MEM_ACCESS_RIGHT_MASK));
|
|
ret = modify_region(vm, region);
|
|
|
|
dev_dbg(acrn_dev.this_device,
|
|
"%s: user-GPA[%p] service-GPA[%p] size[0x%llx].\n",
|
|
__func__, (void *)user_gpa, (void *)service_gpa, size);
|
|
kfree(region);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* acrn_mm_region_del() - Del the EPT mapping of a memory region.
|
|
* @vm: User VM.
|
|
* @user_gpa: A GPA of the User VM.
|
|
* @size: Size of the region.
|
|
*
|
|
* Return: 0 on success, <0 for error.
|
|
*/
|
|
int acrn_mm_region_del(struct acrn_vm *vm, u64 user_gpa, u64 size)
|
|
{
|
|
struct vm_memory_region_op *region;
|
|
int ret = 0;
|
|
|
|
region = kzalloc(sizeof(*region), GFP_KERNEL);
|
|
if (!region)
|
|
return -ENOMEM;
|
|
|
|
region->type = ACRN_MEM_REGION_DEL;
|
|
region->user_vm_pa = user_gpa;
|
|
region->service_vm_pa = 0UL;
|
|
region->size = size;
|
|
region->attr = 0U;
|
|
|
|
ret = modify_region(vm, region);
|
|
|
|
dev_dbg(acrn_dev.this_device, "%s: user-GPA[%p] size[0x%llx].\n",
|
|
__func__, (void *)user_gpa, size);
|
|
kfree(region);
|
|
return ret;
|
|
}
|
|
|
|
int acrn_vm_memseg_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap)
|
|
{
|
|
int ret;
|
|
|
|
if (memmap->type == ACRN_MEMMAP_RAM)
|
|
return acrn_vm_ram_map(vm, memmap);
|
|
|
|
if (memmap->type != ACRN_MEMMAP_MMIO) {
|
|
dev_dbg(acrn_dev.this_device,
|
|
"Invalid memmap type: %u\n", memmap->type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = acrn_mm_region_add(vm, memmap->user_vm_pa,
|
|
memmap->service_vm_pa, memmap->len,
|
|
ACRN_MEM_TYPE_UC, memmap->attr);
|
|
if (ret < 0)
|
|
dev_dbg(acrn_dev.this_device,
|
|
"Add memory region failed, VM[%u]!\n", vm->vmid);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int acrn_vm_memseg_unmap(struct acrn_vm *vm, struct acrn_vm_memmap *memmap)
|
|
{
|
|
int ret;
|
|
|
|
if (memmap->type != ACRN_MEMMAP_MMIO) {
|
|
dev_dbg(acrn_dev.this_device,
|
|
"Invalid memmap type: %u\n", memmap->type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = acrn_mm_region_del(vm, memmap->user_vm_pa, memmap->len);
|
|
if (ret < 0)
|
|
dev_dbg(acrn_dev.this_device,
|
|
"Del memory region failed, VM[%u]!\n", vm->vmid);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* acrn_vm_ram_map() - Create a RAM EPT mapping of User VM.
|
|
* @vm: The User VM pointer
|
|
* @memmap: Info of the EPT mapping
|
|
*
|
|
* Return: 0 on success, <0 for error.
|
|
*/
|
|
int acrn_vm_ram_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap)
|
|
{
|
|
struct vm_memory_region_batch *regions_info;
|
|
int nr_pages, i, order, nr_regions = 0;
|
|
struct vm_memory_mapping *region_mapping;
|
|
struct vm_memory_region_op *vm_region;
|
|
struct page **pages = NULL, *page;
|
|
void *remap_vaddr;
|
|
int ret, pinned;
|
|
u64 user_vm_pa;
|
|
struct vm_area_struct *vma;
|
|
|
|
if (!vm || !memmap)
|
|
return -EINVAL;
|
|
|
|
/* Get the page number of the map region */
|
|
nr_pages = memmap->len >> PAGE_SHIFT;
|
|
if (!nr_pages)
|
|
return -EINVAL;
|
|
|
|
mmap_read_lock(current->mm);
|
|
vma = vma_lookup(current->mm, memmap->vma_base);
|
|
if (vma && ((vma->vm_flags & VM_PFNMAP) != 0)) {
|
|
unsigned long start_pfn, cur_pfn;
|
|
bool writable;
|
|
|
|
if ((memmap->vma_base + memmap->len) > vma->vm_end) {
|
|
mmap_read_unlock(current->mm);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < nr_pages; i++) {
|
|
struct follow_pfnmap_args args = {
|
|
.vma = vma,
|
|
.address = memmap->vma_base + i * PAGE_SIZE,
|
|
};
|
|
|
|
ret = follow_pfnmap_start(&args);
|
|
if (ret)
|
|
break;
|
|
|
|
cur_pfn = args.pfn;
|
|
if (i == 0)
|
|
start_pfn = cur_pfn;
|
|
writable = args.writable;
|
|
follow_pfnmap_end(&args);
|
|
|
|
/* Disallow write access if the PTE is not writable. */
|
|
if (!writable &&
|
|
(memmap->attr & ACRN_MEM_ACCESS_WRITE)) {
|
|
ret = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
/* Disallow refcounted pages. */
|
|
if (pfn_valid(cur_pfn) &&
|
|
!PageReserved(pfn_to_page(cur_pfn))) {
|
|
ret = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
/* Disallow non-contiguous ranges. */
|
|
if (cur_pfn != start_pfn + i) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
mmap_read_unlock(current->mm);
|
|
|
|
if (ret) {
|
|
dev_dbg(acrn_dev.this_device,
|
|
"Failed to lookup PFN at VMA:%p.\n", (void *)memmap->vma_base);
|
|
return ret;
|
|
}
|
|
|
|
return acrn_mm_region_add(vm, memmap->user_vm_pa,
|
|
PFN_PHYS(start_pfn), memmap->len,
|
|
ACRN_MEM_TYPE_WB, memmap->attr);
|
|
}
|
|
mmap_read_unlock(current->mm);
|
|
|
|
pages = vzalloc(array_size(nr_pages, sizeof(*pages)));
|
|
if (!pages)
|
|
return -ENOMEM;
|
|
|
|
/* Lock the pages of user memory map region */
|
|
pinned = pin_user_pages_fast(memmap->vma_base,
|
|
nr_pages, FOLL_WRITE | FOLL_LONGTERM,
|
|
pages);
|
|
if (pinned < 0) {
|
|
ret = pinned;
|
|
goto free_pages;
|
|
} else if (pinned != nr_pages) {
|
|
ret = -EFAULT;
|
|
goto put_pages;
|
|
}
|
|
|
|
/* Create a kernel map for the map region */
|
|
remap_vaddr = vmap(pages, nr_pages, VM_MAP, PAGE_KERNEL);
|
|
if (!remap_vaddr) {
|
|
ret = -ENOMEM;
|
|
goto put_pages;
|
|
}
|
|
|
|
/* Record Service VM va <-> User VM pa mapping */
|
|
mutex_lock(&vm->regions_mapping_lock);
|
|
region_mapping = &vm->regions_mapping[vm->regions_mapping_count];
|
|
if (vm->regions_mapping_count < ACRN_MEM_MAPPING_MAX) {
|
|
region_mapping->pages = pages;
|
|
region_mapping->npages = nr_pages;
|
|
region_mapping->size = memmap->len;
|
|
region_mapping->service_vm_va = remap_vaddr;
|
|
region_mapping->user_vm_pa = memmap->user_vm_pa;
|
|
vm->regions_mapping_count++;
|
|
} else {
|
|
dev_warn(acrn_dev.this_device,
|
|
"Run out of memory mapping slots!\n");
|
|
ret = -ENOMEM;
|
|
mutex_unlock(&vm->regions_mapping_lock);
|
|
goto unmap_no_count;
|
|
}
|
|
mutex_unlock(&vm->regions_mapping_lock);
|
|
|
|
/* Calculate count of vm_memory_region_op */
|
|
for (i = 0; i < nr_pages; i += 1 << order) {
|
|
page = pages[i];
|
|
VM_BUG_ON_PAGE(PageTail(page), page);
|
|
order = compound_order(page);
|
|
nr_regions++;
|
|
}
|
|
|
|
/* Prepare the vm_memory_region_batch */
|
|
regions_info = kzalloc(struct_size(regions_info, regions_op,
|
|
nr_regions), GFP_KERNEL);
|
|
if (!regions_info) {
|
|
ret = -ENOMEM;
|
|
goto unmap_kernel_map;
|
|
}
|
|
regions_info->regions_num = nr_regions;
|
|
|
|
/* Fill each vm_memory_region_op */
|
|
vm_region = regions_info->regions_op;
|
|
regions_info->vmid = vm->vmid;
|
|
regions_info->regions_gpa = virt_to_phys(vm_region);
|
|
user_vm_pa = memmap->user_vm_pa;
|
|
for (i = 0; i < nr_pages; i += 1 << order) {
|
|
u32 region_size;
|
|
|
|
page = pages[i];
|
|
VM_BUG_ON_PAGE(PageTail(page), page);
|
|
order = compound_order(page);
|
|
region_size = PAGE_SIZE << order;
|
|
vm_region->type = ACRN_MEM_REGION_ADD;
|
|
vm_region->user_vm_pa = user_vm_pa;
|
|
vm_region->service_vm_pa = page_to_phys(page);
|
|
vm_region->size = region_size;
|
|
vm_region->attr = (ACRN_MEM_TYPE_WB & ACRN_MEM_TYPE_MASK) |
|
|
(memmap->attr & ACRN_MEM_ACCESS_RIGHT_MASK);
|
|
|
|
vm_region++;
|
|
user_vm_pa += region_size;
|
|
}
|
|
|
|
/* Inform the ACRN Hypervisor to set up EPT mappings */
|
|
ret = hcall_set_memory_regions(virt_to_phys(regions_info));
|
|
if (ret < 0) {
|
|
dev_dbg(acrn_dev.this_device,
|
|
"Failed to set regions, VM[%u]!\n", vm->vmid);
|
|
goto unset_region;
|
|
}
|
|
kfree(regions_info);
|
|
|
|
dev_dbg(acrn_dev.this_device,
|
|
"%s: VM[%u] service-GVA[%p] user-GPA[%p] size[0x%llx]\n",
|
|
__func__, vm->vmid,
|
|
remap_vaddr, (void *)memmap->user_vm_pa, memmap->len);
|
|
return ret;
|
|
|
|
unset_region:
|
|
kfree(regions_info);
|
|
unmap_kernel_map:
|
|
mutex_lock(&vm->regions_mapping_lock);
|
|
vm->regions_mapping_count--;
|
|
mutex_unlock(&vm->regions_mapping_lock);
|
|
unmap_no_count:
|
|
vunmap(remap_vaddr);
|
|
put_pages:
|
|
for (i = 0; i < pinned; i++)
|
|
unpin_user_page(pages[i]);
|
|
free_pages:
|
|
vfree(pages);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* acrn_vm_all_ram_unmap() - Destroy a RAM EPT mapping of User VM.
|
|
* @vm: The User VM
|
|
*/
|
|
void acrn_vm_all_ram_unmap(struct acrn_vm *vm)
|
|
{
|
|
struct vm_memory_mapping *region_mapping;
|
|
int i, j;
|
|
|
|
mutex_lock(&vm->regions_mapping_lock);
|
|
for (i = 0; i < vm->regions_mapping_count; i++) {
|
|
region_mapping = &vm->regions_mapping[i];
|
|
vunmap(region_mapping->service_vm_va);
|
|
for (j = 0; j < region_mapping->npages; j++)
|
|
unpin_user_page(region_mapping->pages[j]);
|
|
vfree(region_mapping->pages);
|
|
}
|
|
mutex_unlock(&vm->regions_mapping_lock);
|
|
}
|