pKVM need to reserve its own memory for its data or runtime allocation, such as pkvm data structure, page allocator data structure (vmemmap), VMX pages, and MMU/EPT page table pages. Implement hyp_total_reserve_pages to calculate total reserve page numbers needed by pKVM. Virtual memory map (vmemmap) for page allocator is calculated based on system memory size. 3 extra pages for VMX - vmcs area to support VMXON, vmcs page to support run host vcpu, and msr_bitmap to support msr emulation. Consider the worst case for page table allocation pages. For host EPT, allow extra 1 GiB for MMIO mappings. Signed-off-by: Shaoqin Huang <shaoqin.huang@xxxxxxxxx> Signed-off-by: Chuanxiao Dong <chuanxiao.dong@xxxxxxxxx> Signed-off-by: Jason Chen CJ <jason.cj.chen@xxxxxxxxx> --- arch/x86/include/asm/kvm_pkvm.h | 94 ++++++++++++++++++++++++++++ arch/x86/kvm/vmx/pkvm/hyp/memory.c | 4 ++ arch/x86/kvm/vmx/pkvm/include/pkvm.h | 2 + arch/x86/kvm/vmx/pkvm/pkvm_host.c | 14 +++++ 4 files changed, 114 insertions(+) diff --git a/arch/x86/include/asm/kvm_pkvm.h b/arch/x86/include/asm/kvm_pkvm.h index 480051f28ebc..30f7f805bcb8 100644 --- a/arch/x86/include/asm/kvm_pkvm.h +++ b/arch/x86/include/asm/kvm_pkvm.h @@ -8,6 +8,15 @@ #ifdef CONFIG_PKVM_INTEL +#include <linux/memblock.h> +#include <asm/pkvm_image.h> + +#define HYP_MEMBLOCK_REGIONS 128 +#define PKVM_PGTABLE_MAX_LEVELS 5U + +extern struct memblock_region pkvm_sym(hyp_memory)[]; +extern unsigned int pkvm_sym(hyp_memblock_nr); + void *pkvm_phys_to_virt(unsigned long phys); unsigned long pkvm_virt_to_phys(void *virt); @@ -18,6 +27,91 @@ unsigned long pkvm_virt_to_phys(void *virt); #define __hyp_pa __pkvm_pa #define __hyp_va __pkvm_va +static inline unsigned long __pkvm_pgtable_max_pages(unsigned long nr_pages) +{ + unsigned long total = 0, i; + + /* Provision the worst case */ + for (i = 0; i < PKVM_PGTABLE_MAX_LEVELS; i++) { + nr_pages = DIV_ROUND_UP(nr_pages, PTRS_PER_PTE); + total += nr_pages; + } + + return total; +} + +static inline unsigned long __pkvm_pgtable_total_pages(void) +{ + unsigned long total = 0, i; + + for (i = 0; i < pkvm_sym(hyp_memblock_nr); i++) { + struct memblock_region *reg = &pkvm_sym(hyp_memory)[i]; + + total += __pkvm_pgtable_max_pages(reg->size >> PAGE_SHIFT); + } + + return total; +} + +static inline unsigned long host_ept_pgtable_pages(void) +{ + unsigned long res; + + /* + * Include an extra 16 pages to safely upper-bound the worst case of + * concatenated pgds. + */ + res = __pkvm_pgtable_total_pages() + 16; + + /* Allow 1 GiB for MMIO mappings */ + res += __pkvm_pgtable_max_pages(SZ_1G >> PAGE_SHIFT); + + return res; +} + +static inline unsigned long pkvm_mmu_pgtable_pages(void) +{ + unsigned long res; + + res = __pkvm_pgtable_total_pages(); + + return res; +} + +static inline unsigned long pkvm_vmemmap_memblock_size(struct memblock_region *reg, + size_t vmemmap_entry_size) +{ + unsigned long nr_pages = reg->size >> PAGE_SHIFT; + unsigned long start, end; + + /* Translate the pfn to the vmemmap entry */ + start = (reg->base >> PAGE_SHIFT) * vmemmap_entry_size; + end = start + nr_pages * vmemmap_entry_size; + start = ALIGN_DOWN(start, PAGE_SIZE); + end = ALIGN(end, PAGE_SIZE); + + return end - start; +} + +static inline unsigned long pkvm_vmemmap_pages(size_t vmemmap_entry_size) +{ + unsigned long total_size = 0, i; + + for (i = 0; i < pkvm_sym(hyp_memblock_nr); i++) { + total_size += pkvm_vmemmap_memblock_size(&pkvm_sym(hyp_memory)[i], + vmemmap_entry_size); + } + + return total_size >> PAGE_SHIFT; +} + +static inline unsigned long pkvm_data_struct_pages(unsigned long global_pgs, + unsigned long percpu_pgs, int num_cpus) +{ + return (percpu_pgs * num_cpus + global_pgs); +} + +u64 hyp_total_reserve_pages(void); #endif #endif diff --git a/arch/x86/kvm/vmx/pkvm/hyp/memory.c b/arch/x86/kvm/vmx/pkvm/hyp/memory.c index 62dd80947d8e..eb913cf08691 100644 --- a/arch/x86/kvm/vmx/pkvm/hyp/memory.c +++ b/arch/x86/kvm/vmx/pkvm/hyp/memory.c @@ -4,10 +4,14 @@ */ #include <linux/types.h> +#include <asm/kvm_pkvm.h> unsigned long __page_base_offset; unsigned long __symbol_base_offset; +unsigned int hyp_memblock_nr; +struct memblock_region hyp_memory[HYP_MEMBLOCK_REGIONS]; + void *pkvm_phys_to_virt(unsigned long phys) { return (void *)__page_base_offset + phys; diff --git a/arch/x86/kvm/vmx/pkvm/include/pkvm.h b/arch/x86/kvm/vmx/pkvm/include/pkvm.h index b344165511f7..1c2b70c3788e 100644 --- a/arch/x86/kvm/vmx/pkvm/include/pkvm.h +++ b/arch/x86/kvm/vmx/pkvm/include/pkvm.h @@ -47,6 +47,8 @@ struct pkvm_hyp { #define PKVM_PAGES (ALIGN(sizeof(struct pkvm_hyp), PAGE_SIZE) >> PAGE_SHIFT) #define PKVM_PCPU_PAGES (ALIGN(sizeof(struct pkvm_pcpu), PAGE_SIZE) >> PAGE_SHIFT) #define PKVM_HOST_VCPU_PAGES (ALIGN(sizeof(struct pkvm_host_vcpu), PAGE_SIZE) >> PAGE_SHIFT) +#define PKVM_VMCS_PAGES 3 /*vmxarea+vmcs+msr_bitmap*/ +#define PKVM_PERCPU_PAGES (PKVM_PCPU_PAGES + PKVM_HOST_VCPU_PAGES + PKVM_VMCS_PAGES) extern char __pkvm_text_start[], __pkvm_text_end[]; diff --git a/arch/x86/kvm/vmx/pkvm/pkvm_host.c b/arch/x86/kvm/vmx/pkvm/pkvm_host.c index 9705aebaab2e..655ee8da2ac2 100644 --- a/arch/x86/kvm/vmx/pkvm/pkvm_host.c +++ b/arch/x86/kvm/vmx/pkvm/pkvm_host.c @@ -6,8 +6,10 @@ #include <linux/kernel.h> #include <linux/module.h> #include <asm/trapnr.h> +#include <asm/kvm_pkvm.h> #include <pkvm.h> +#include "pkvm_constants.h" MODULE_LICENSE("GPL"); @@ -29,6 +31,18 @@ static struct gdt_page pkvm_gdt_page = { }, }; +u64 hyp_total_reserve_pages(void) +{ + u64 total; + + total = pkvm_data_struct_pages(PKVM_PAGES, PKVM_PERCPU_PAGES, num_possible_cpus()); + total += pkvm_vmemmap_pages(PKVM_VMEMMAP_ENTRY_SIZE); + total += pkvm_mmu_pgtable_pages(); + total += host_ept_pgtable_pages(); + + return total; +} + static void *pkvm_early_alloc_contig(int pages) { return alloc_pages_exact(pages << PAGE_SHIFT, GFP_KERNEL | __GFP_ZERO); -- 2.25.1