From 142e96f389e64ce3a3d95fd004bded628fe4fb31 Mon Sep 17 00:00:00 2001 From: Yang Wencheng Date: Tue, 10 Mar 2026 12:20:17 +0000 Subject: [PATCH] anolis: arch/x86/mm: reserve memblock for CSV3 guest ANBZ: #32874 Support to reserve memblock for CSV3 during boot stage, typical value of the block size and alignment is 1G, so that it matches the size of CSV3 hardware protection rules. User can reserve memory by passing "csv3_guest_rsv_mb=nG" to kernel command line. Signed-off-by: YangWencheng Cc: hygon-arch@list.openanolis.cn --- arch/x86/include/asm/csv.h | 9 + arch/x86/kernel/setup.c | 3 + arch/x86/mm/Makefile | 1 + arch/x86/mm/csv_guest_mem.c | 332 ++++++++++++++++++++++++++++++++++++ 4 files changed, 345 insertions(+) create mode 100644 arch/x86/mm/csv_guest_mem.c diff --git a/arch/x86/include/asm/csv.h b/arch/x86/include/asm/csv.h index 7846861b9d35..05251292434f 100644 --- a/arch/x86/include/asm/csv.h +++ b/arch/x86/include/asm/csv.h @@ -57,6 +57,11 @@ uint32_t csv_get_smr_entry_shift(void); int csv3_issue_request_report(phys_addr_t paddr, size_t size); int csv3_issue_request_rtmr(void *req_buffer, size_t buffer_size); +void __init early_csv_guest_mem_init(void); +phys_addr_t csv3_alloc_mem_block(void); +void csv3_free_mem_block(phys_addr_t phys_addr); +size_t csv3_get_mem_block_size(void); + #else /* !CONFIG_HYGON_CSV */ #define csv_smr NULL @@ -81,6 +86,10 @@ static inline uint32_t csv_get_smr_entry_shift(void) { return 0; } static inline int csv3_issue_request_report(phys_addr_t paddr, size_t size) { return -EIO; } static inline int csv3_issue_request_rtmr(void *req_buffer, size_t buffer_size) { return -ENODEV; } +static inline void __init early_csv_guest_mem_init(void) { } +static inline phys_addr_t csv3_alloc_mem_block(void) { return 0; } +static inline void csv3_free_mem_block(phys_addr_t phys_addr) { } +static inline size_t csv3_get_mem_block_size(void) { return 0; } #endif /* CONFIG_HYGON_CSV */ #endif diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 582cb5998ca9..e025ac7935c6 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1223,6 +1223,9 @@ void __init setup_arch(char **cmdline_p) initmem_init(); #ifdef CONFIG_HYGON_CSV + /* CSV guest memory specific initialization */ + early_csv_guest_mem_init(); + early_csv_reserve_mem(); #endif dma_contiguous_reserve(max_pfn_mapped << PAGE_SHIFT); diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 08c4dbe1e12b..8c3f15303152 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -62,3 +62,4 @@ obj-$(CONFIG_AMD_MEM_ENCRYPT) += mem_encrypt_identity.o obj-$(CONFIG_AMD_MEM_ENCRYPT) += mem_encrypt_boot.o obj-$(CONFIG_HYGON_CSV) += csv.o +obj-$(CONFIG_HYGON_CSV) += csv_guest_mem.o diff --git a/arch/x86/mm/csv_guest_mem.c b/arch/x86/mm/csv_guest_mem.c new file mode 100644 index 000000000000..7d4736503c7f --- /dev/null +++ b/arch/x86/mm/csv_guest_mem.c @@ -0,0 +1,332 @@ +/* + * SPDX-License-Identifier: GPL-2.0-only + * + * Hygon China Secure Virtualization (CSV) + * + * Copyright (C) Hygon Info Technologies Ltd. + * + * Description: The file is used to reserve memblock for CSV3 guest. + * + * Author: Wencheng Yang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CSV3_MEM_BLOCK_SIZE (SZ_1G) +#define CSV3_MEM_BLOCK_ALIGN (SZ_1G) + +/** + * The struct is used to record the memory block information. + * If the block is the header of the large block, the head is true. + * The size is the size of the large block, each sub block size is + * mem_block->block_size. + */ +struct block { + phys_addr_t start; + unsigned long size; // the size of the large block + bool used; + bool head; // head of the block +}; + +struct mem_block { + unsigned long block_align; + unsigned long block_size; + unsigned long nr_blocks; + unsigned long nr_free; + bool decrypted; + bool decrypt_failed; + struct block blocks[]; +}; + +static struct mem_block *csv3_mem_block; + +static phys_addr_t __initdata size_cmdline = -1; + +static DEFINE_MUTEX(lock); + +/** + * Config string format: csv3_guest_rsv_mb=size + * For example: csv3_guest_rsv_mb=1G + * csv3_guest_rsv_mb=2048M + * + * size: the size of memory to reserve, it should be multiple + * of CSV3_MEM_BLOCK_SIZE, i.e. 1G. + */ +static int __init early_csv3_reserve_mem_block(char *p) +{ + if (!p) + return -EINVAL; + + size_cmdline = memparse(p, &p); + + return 0; +} + +early_param("csv3_guest_rsv_mb", early_csv3_reserve_mem_block); + +/** + * csv3_reserve_mem_block() - reserve memblock for csv3 guest. + */ +static void __init csv3_reserve_mem_block(void) +{ + unsigned long csv3_mem_size; + unsigned long size, reserved_size, meta_size; + unsigned long block_cnt, reserved_cnt, try_cnt; + size_t align = CSV3_MEM_BLOCK_ALIGN; + size_t block_size = CSV3_MEM_BLOCK_SIZE; + phys_addr_t base; + int i; + + if (!cc_platform_has_csv3()) + return; + + if (size_cmdline == -1 || size_cmdline == 0) + return; + + csv3_mem_size = size_cmdline; + if (size_cmdline % align) { + csv3_mem_size = ALIGN(size_cmdline, block_size); + pr_warn("csv3_guest_rsv_mb: size must be multiple of %u MB, " + "align up to %lu MB\n", + CSV3_MEM_BLOCK_ALIGN / SZ_1M, csv3_mem_size / SZ_1M); + } + + block_cnt = csv3_mem_size / block_size; + meta_size = sizeof(struct mem_block) + block_cnt * sizeof(struct block); + csv3_mem_block = memblock_alloc_node(meta_size, SMP_CACHE_BYTES, + NUMA_NO_NODE); + if (!csv3_mem_block) { + pr_err("csv3_guest_rsv_mb: fail to allocate size 0x%lx from " + "memblock\n", + meta_size); + return; + } + + memset(csv3_mem_block, 0, meta_size); + csv3_mem_block->block_size = block_size; + csv3_mem_block->block_align = align; + + try_cnt = 0; + reserved_size = 0; + reserved_cnt = 0; + while (reserved_size < csv3_mem_size) { + size = csv3_mem_size - reserved_size - try_cnt * SZ_1G; + base = memblock_phys_alloc_try_nid(size, + csv3_mem_block->block_align, + NUMA_NO_NODE); + if (!base) { + pr_warn("csv3_guest_rsv_mb: reserve memblock failed\n"); + // can't alloc any more, no retry. + if (size <= CSV3_MEM_BLOCK_SIZE) + goto fail_free; + + try_cnt++; + continue; + } else { + /* success, reset try_cnt */ + try_cnt = 0; + } + + pr_debug("csv3_guest_rsv_mb: reserve memblock[%ld] 0x%lx MB " + "memory, pa 0x%llx\n", + reserved_cnt, size / SZ_1M, base); + + block_cnt = size / csv3_mem_block->block_size; + csv3_mem_block->blocks[reserved_cnt].head = true; + csv3_mem_block->blocks[reserved_cnt].size = size; + for (i = 0; i < block_cnt; i++) { + csv3_mem_block->blocks[reserved_cnt].start = base; + csv3_mem_block->blocks[reserved_cnt].used = false; + csv3_mem_block->nr_blocks++; + csv3_mem_block->nr_free++; + base += csv3_mem_block->block_size; + reserved_cnt++; + } + + reserved_size += size; + } + + pr_info("csv3_guest_rsv_mb: reserved %lu MB memory from memblock\n", + reserved_size / SZ_1M); + + return; + +fail_free: + /* find the head block, free the block */ + for (i = 0; i < reserved_cnt; i++) { + if (!csv3_mem_block->blocks[i].head) + continue; + base = csv3_mem_block->blocks[i].start; + size = csv3_mem_block->blocks[i].size; + memblock_free(base, size); + } + + memblock_free(__pa(csv3_mem_block), meta_size); + csv3_mem_block = NULL; +} + +static bool decrypt_mem_block(void) +{ + unsigned long size; + unsigned long virt; + phys_addr_t phys_addr; + int i, j; + + for (i = 0; i < csv3_mem_block->nr_blocks; i++) { + if (!csv3_mem_block->blocks[i].head) + continue; + phys_addr = csv3_mem_block->blocks[i].start; + size = csv3_mem_block->blocks[i].size; + virt = (unsigned long)phys_to_virt(phys_addr); + if (set_memory_decrypted(virt, size >> PAGE_SHIFT)) { + pr_err("csv3_guest_rsv_mb: decrypt pa 0x%llx " + "size 0x%lx va 0x%llx failed\n", + phys_addr, size, (u64)virt); + break; + } + } + + if (i == csv3_mem_block->nr_blocks) + return true; + + // decrypt failed, restore to encrypted state + for (j = 0; j < i; j++) { + if (!csv3_mem_block->blocks[j].head) + continue; + + phys_addr = csv3_mem_block->blocks[j].start; + size = csv3_mem_block->blocks[j].size; + virt = (unsigned long)phys_to_virt(phys_addr); + set_memory_encrypted(virt, size >> PAGE_SHIFT); + } + + return false; +} + +phys_addr_t csv3_alloc_mem_block(void) +{ + phys_addr_t phys_addr = 0; + int i; + + if (!cc_platform_has_csv3()) + return 0; + + if (!csv3_mem_block) + return 0; + + mutex_lock(&lock); + + if (unlikely(csv3_mem_block->decrypt_failed)) { + mutex_unlock(&lock); + return 0; + } + + if (unlikely(!csv3_mem_block->decrypted)) { + if (!decrypt_mem_block()) { + csv3_mem_block->decrypt_failed = true; + mutex_unlock(&lock); + return 0; + } + + csv3_mem_block->decrypted = true; + } + + if (csv3_mem_block->nr_free == 0) + goto out; + + // iterate csv3_mem_block->blocks to find a free block. + for (i = 0; i < csv3_mem_block->nr_blocks; i++) { + if (csv3_mem_block->blocks[i].used) + continue; + + csv3_mem_block->blocks[i].used = true; + csv3_mem_block->nr_free--; + break; + } + + if (i == csv3_mem_block->nr_blocks) + goto out; + else + phys_addr = csv3_mem_block->blocks[i].start; +out: + mutex_unlock(&lock); + + pr_debug("csv3_guest_rsv_mb: allocate 0x%lx MB memory, pa 0x%llx\n", + phys_addr ? csv3_mem_block->block_size / SZ_1M : 0, phys_addr); + + return phys_addr; +} + +EXPORT_SYMBOL_GPL(csv3_alloc_mem_block); + +void csv3_free_mem_block(phys_addr_t phys_addr) +{ + int i; + bool free_done = false; + + if (!cc_platform_has_csv3()) + return; + + if (!csv3_mem_block || !phys_addr) + return; + + mutex_lock(&lock); + + if (csv3_mem_block->nr_free >= csv3_mem_block->nr_blocks) { + mutex_unlock(&lock); + pr_err("csv3_guest_rsv_mb: all blocks are free, " + "please check\n"); + return; + } + + // find the block and mark it as free. + for (i = 0; i < csv3_mem_block->nr_blocks; i++) { + if (!csv3_mem_block->blocks[i].used || + csv3_mem_block->blocks[i].start != phys_addr) + continue; + + csv3_mem_block->blocks[i].used = false; + + csv3_mem_block->nr_free++; + free_done = true; + break; + } + + mutex_unlock(&lock); + + if (free_done) { + pr_debug("csv3_guest_rsv_mb: free 0x%lx MB memory, pa 0x%llx\n", + csv3_mem_block->block_size / SZ_1M, phys_addr); + } else { + pr_err("csv3_guest_rsv_mb: fail to free 0x%lx MB memory, " + "pa 0x%llx\n", + csv3_mem_block->block_size / SZ_1M, phys_addr); + } +} + +EXPORT_SYMBOL_GPL(csv3_free_mem_block); + +size_t csv3_get_mem_block_size(void) +{ + if (!csv3_mem_block) + return 0; + + return csv3_mem_block->block_size; +} + +EXPORT_SYMBOL_GPL(csv3_get_mem_block_size); + +void __init early_csv_guest_mem_init(void) +{ + csv3_reserve_mem_block(); +} + -- Gitee