162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 - Google LLC
462306a36Sopenharmony_ci * Author: Quentin Perret <qperret@google.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#ifndef __ARM64_KVM_PKVM_H__
762306a36Sopenharmony_ci#define __ARM64_KVM_PKVM_H__
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/arm_ffa.h>
1062306a36Sopenharmony_ci#include <linux/memblock.h>
1162306a36Sopenharmony_ci#include <linux/scatterlist.h>
1262306a36Sopenharmony_ci#include <asm/kvm_pgtable.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* Maximum number of VMs that can co-exist under pKVM. */
1562306a36Sopenharmony_ci#define KVM_MAX_PVMS 255
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define HYP_MEMBLOCK_REGIONS 128
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciint pkvm_init_host_vm(struct kvm *kvm);
2062306a36Sopenharmony_ciint pkvm_create_hyp_vm(struct kvm *kvm);
2162306a36Sopenharmony_civoid pkvm_destroy_hyp_vm(struct kvm *kvm);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciextern struct memblock_region kvm_nvhe_sym(hyp_memory)[];
2462306a36Sopenharmony_ciextern unsigned int kvm_nvhe_sym(hyp_memblock_nr);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic inline unsigned long
2762306a36Sopenharmony_cihyp_vmemmap_memblock_size(struct memblock_region *reg, size_t vmemmap_entry_size)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	unsigned long nr_pages = reg->size >> PAGE_SHIFT;
3062306a36Sopenharmony_ci	unsigned long start, end;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	start = (reg->base >> PAGE_SHIFT) * vmemmap_entry_size;
3362306a36Sopenharmony_ci	end = start + nr_pages * vmemmap_entry_size;
3462306a36Sopenharmony_ci	start = ALIGN_DOWN(start, PAGE_SIZE);
3562306a36Sopenharmony_ci	end = ALIGN(end, PAGE_SIZE);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return end - start;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline unsigned long hyp_vmemmap_pages(size_t vmemmap_entry_size)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	unsigned long res = 0, i;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	for (i = 0; i < kvm_nvhe_sym(hyp_memblock_nr); i++) {
4562306a36Sopenharmony_ci		res += hyp_vmemmap_memblock_size(&kvm_nvhe_sym(hyp_memory)[i],
4662306a36Sopenharmony_ci						 vmemmap_entry_size);
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return res >> PAGE_SHIFT;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic inline unsigned long hyp_vm_table_pages(void)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return PAGE_ALIGN(KVM_MAX_PVMS * sizeof(void *)) >> PAGE_SHIFT;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic inline unsigned long __hyp_pgtable_max_pages(unsigned long nr_pages)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	unsigned long total = 0, i;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* Provision the worst case scenario */
6262306a36Sopenharmony_ci	for (i = 0; i < KVM_PGTABLE_MAX_LEVELS; i++) {
6362306a36Sopenharmony_ci		nr_pages = DIV_ROUND_UP(nr_pages, PTRS_PER_PTE);
6462306a36Sopenharmony_ci		total += nr_pages;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return total;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic inline unsigned long __hyp_pgtable_total_pages(void)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	unsigned long res = 0, i;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Cover all of memory with page-granularity */
7562306a36Sopenharmony_ci	for (i = 0; i < kvm_nvhe_sym(hyp_memblock_nr); i++) {
7662306a36Sopenharmony_ci		struct memblock_region *reg = &kvm_nvhe_sym(hyp_memory)[i];
7762306a36Sopenharmony_ci		res += __hyp_pgtable_max_pages(reg->size >> PAGE_SHIFT);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return res;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic inline unsigned long hyp_s1_pgtable_pages(void)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	unsigned long res;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	res = __hyp_pgtable_total_pages();
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Allow 1 GiB for private mappings */
9062306a36Sopenharmony_ci	res += __hyp_pgtable_max_pages(SZ_1G >> PAGE_SHIFT);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return res;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic inline unsigned long host_s2_pgtable_pages(void)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	unsigned long res;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/*
10062306a36Sopenharmony_ci	 * Include an extra 16 pages to safely upper-bound the worst case of
10162306a36Sopenharmony_ci	 * concatenated pgds.
10262306a36Sopenharmony_ci	 */
10362306a36Sopenharmony_ci	res = __hyp_pgtable_total_pages() + 16;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* Allow 1 GiB for MMIO mappings */
10662306a36Sopenharmony_ci	res += __hyp_pgtable_max_pages(SZ_1G >> PAGE_SHIFT);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return res;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define KVM_FFA_MBOX_NR_PAGES	1
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic inline unsigned long hyp_ffa_proxy_pages(void)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	size_t desc_max;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/*
11862306a36Sopenharmony_ci	 * The hypervisor FFA proxy needs enough memory to buffer a fragmented
11962306a36Sopenharmony_ci	 * descriptor returned from EL3 in response to a RETRIEVE_REQ call.
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	desc_max = sizeof(struct ffa_mem_region) +
12262306a36Sopenharmony_ci		   sizeof(struct ffa_mem_region_attributes) +
12362306a36Sopenharmony_ci		   sizeof(struct ffa_composite_mem_region) +
12462306a36Sopenharmony_ci		   SG_MAX_SEGMENTS * sizeof(struct ffa_mem_region_addr_range);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* Plus a page each for the hypervisor's RX and TX mailboxes. */
12762306a36Sopenharmony_ci	return (2 * KVM_FFA_MBOX_NR_PAGES) + DIV_ROUND_UP(desc_max, PAGE_SIZE);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#endif	/* __ARM64_KVM_PKVM_H__ */
131