368 lines
8.5 KiB
C
368 lines
8.5 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Hygon China Secure Virtualization (CSV)
|
|
*
|
|
* Copyright (C) Hygon Info Technologies Ltd.
|
|
*
|
|
* Author: Jiang Xin <jiangxin@hygon.cn>
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/module.h>
|
|
#include <linux/psp-sev.h>
|
|
#include <linux/cma.h>
|
|
#include <linux/minmax.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <asm/io.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/set_memory.h>
|
|
#include <asm/csv.h>
|
|
|
|
#undef pr_fmt
|
|
#define pr_fmt(fmt) "CSV-CMA: " fmt
|
|
|
|
#define NUM_SMR_ENTRIES (8 * 1024)
|
|
#define CSV_CMA_SHIFT PUD_SHIFT
|
|
#define CSV_CMA_SIZE (1 << CSV_CMA_SHIFT)
|
|
#define MIN_SMR_ENTRY_SHIFT 23
|
|
#define CSV_SMR_INFO_SIZE (nr_node_ids * sizeof(struct csv_mem))
|
|
|
|
struct used_hugetlb_migration_control {
|
|
spinlock_t lock;
|
|
int enabled_counts;
|
|
int last_value;
|
|
};
|
|
|
|
static struct used_hugetlb_migration_control control;
|
|
|
|
/* 0 percent of total memory by default*/
|
|
static unsigned char csv_mem_percentage;
|
|
static unsigned long csv_mem_size;
|
|
|
|
static int __init cmdline_parse_csv_mem_size(char *str)
|
|
{
|
|
unsigned long size;
|
|
char *endp;
|
|
|
|
if (str) {
|
|
size = memparse(str, &endp);
|
|
csv_mem_size = size;
|
|
if (!csv_mem_size)
|
|
csv_mem_percentage = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
early_param("csv_mem_size", cmdline_parse_csv_mem_size);
|
|
|
|
static int __init cmdline_parse_csv_mem_percentage(char *str)
|
|
{
|
|
unsigned char percentage;
|
|
int ret;
|
|
|
|
if (!str)
|
|
return 0;
|
|
|
|
ret = kstrtou8(str, 10, &percentage);
|
|
if (!ret) {
|
|
csv_mem_percentage = min_t(unsigned char, percentage, 80);
|
|
if (csv_mem_percentage != percentage)
|
|
pr_warn("csv_mem_percentage is limited to 80.\n");
|
|
} else {
|
|
/* Disable CSV CMA. */
|
|
csv_mem_percentage = 0;
|
|
pr_err("csv_mem_percentage is invalid. (0 - 80) is expected.\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
early_param("csv_mem_percentage", cmdline_parse_csv_mem_percentage);
|
|
|
|
struct csv_mem *csv_smr;
|
|
EXPORT_SYMBOL_GPL(csv_smr);
|
|
|
|
unsigned int csv_smr_num;
|
|
EXPORT_SYMBOL_GPL(csv_smr_num);
|
|
|
|
struct csv_cma {
|
|
int fast;
|
|
struct cma *cma;
|
|
};
|
|
|
|
struct cma_array {
|
|
unsigned long count;
|
|
struct csv_cma csv_cma[];
|
|
};
|
|
|
|
static unsigned int smr_entry_shift;
|
|
static struct cma_array *csv_contiguous_pernuma_area[MAX_NUMNODES];
|
|
|
|
static void csv_set_smr_entry_shift(unsigned int shift)
|
|
{
|
|
smr_entry_shift = max_t(unsigned int, shift, MIN_SMR_ENTRY_SHIFT);
|
|
pr_info("SMR entry size is 0x%x\n", 1 << smr_entry_shift);
|
|
}
|
|
|
|
unsigned int csv_get_smr_entry_shift(void)
|
|
{
|
|
return smr_entry_shift;
|
|
}
|
|
EXPORT_SYMBOL_GPL(csv_get_smr_entry_shift);
|
|
|
|
static unsigned long __init present_pages_in_node(int nid)
|
|
{
|
|
unsigned long range_start_pfn, range_end_pfn;
|
|
unsigned long nr_present = 0;
|
|
int i;
|
|
|
|
for_each_mem_pfn_range(i, nid, &range_start_pfn, &range_end_pfn, NULL)
|
|
nr_present += range_end_pfn - range_start_pfn;
|
|
|
|
return nr_present;
|
|
}
|
|
|
|
static phys_addr_t __init csv_early_percent_memory_on_node(int nid)
|
|
{
|
|
return (present_pages_in_node(nid) * csv_mem_percentage / 100) << PAGE_SHIFT;
|
|
}
|
|
|
|
void __init csv_cma_reserve_mem(void)
|
|
{
|
|
int node, i;
|
|
unsigned long size;
|
|
int idx = 0;
|
|
int count;
|
|
int cma_array_size;
|
|
unsigned long max_spanned_size = 0;
|
|
|
|
csv_smr = memblock_alloc_node(CSV_SMR_INFO_SIZE, SMP_CACHE_BYTES, NUMA_NO_NODE);
|
|
if (!csv_smr) {
|
|
pr_err("Fail to allocate csv_smr\n");
|
|
return;
|
|
}
|
|
|
|
for_each_node_state(node, N_ONLINE) {
|
|
int ret;
|
|
char name[CMA_MAX_NAME];
|
|
struct cma_array *array;
|
|
unsigned long spanned_size;
|
|
unsigned long start = 0, end = 0;
|
|
struct csv_cma *csv_cma;
|
|
|
|
size = csv_early_percent_memory_on_node(node);
|
|
count = DIV_ROUND_UP(size, 1 << CSV_CMA_SHIFT);
|
|
if (!count)
|
|
continue;
|
|
|
|
cma_array_size = count * sizeof(*csv_cma) + sizeof(*array);
|
|
array = memblock_alloc_node(cma_array_size, SMP_CACHE_BYTES, NUMA_NO_NODE);
|
|
if (!array) {
|
|
pr_err("Fail to allocate cma_array\n");
|
|
continue;
|
|
}
|
|
|
|
array->count = 0;
|
|
csv_contiguous_pernuma_area[node] = array;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
csv_cma = &array->csv_cma[i];
|
|
csv_cma->fast = 1;
|
|
snprintf(name, sizeof(name), "csv-n%dc%d", node, i);
|
|
ret = cma_declare_contiguous_nid(0, CSV_CMA_SIZE, 0,
|
|
1 << CSV_MR_ALIGN_BITS, PMD_SHIFT - PAGE_SHIFT,
|
|
false, name, &(csv_cma->cma), node);
|
|
if (ret) {
|
|
pr_warn("Fail to reserve memory size 0x%x node %d\n",
|
|
1 << CSV_CMA_SHIFT, node);
|
|
break;
|
|
}
|
|
|
|
if (start > cma_get_base(csv_cma->cma) || !start)
|
|
start = cma_get_base(csv_cma->cma);
|
|
|
|
if (end < cma_get_base(csv_cma->cma) + cma_get_size(csv_cma->cma))
|
|
end = cma_get_base(csv_cma->cma) + cma_get_size(csv_cma->cma);
|
|
}
|
|
|
|
if (!i)
|
|
continue;
|
|
|
|
array->count = i;
|
|
spanned_size = end - start;
|
|
if (spanned_size > max_spanned_size)
|
|
max_spanned_size = spanned_size;
|
|
|
|
csv_smr[idx].start = start;
|
|
csv_smr[idx].size = end - start;
|
|
idx++;
|
|
|
|
pr_info("Node %d - reserve size 0x%016lx, (expected size 0x%016lx)\n",
|
|
node, (unsigned long)i * CSV_CMA_SIZE, size);
|
|
}
|
|
|
|
csv_smr_num = idx;
|
|
WARN_ON((max_spanned_size / NUM_SMR_ENTRIES) < 1);
|
|
if (likely((max_spanned_size / NUM_SMR_ENTRIES) >= 1))
|
|
csv_set_smr_entry_shift(ilog2(max_spanned_size / NUM_SMR_ENTRIES - 1) + 1);
|
|
}
|
|
|
|
#define CSV_CMA_AREAS 2458
|
|
void __init early_csv_reserve_mem(void)
|
|
{
|
|
unsigned long total_pages;
|
|
|
|
if (!(boot_cpu_data.x86_vendor == X86_VENDOR_HYGON &&
|
|
boot_cpu_data.x86_model >= 0x4))
|
|
return;
|
|
|
|
if (cma_alloc_areas(CSV_CMA_AREAS))
|
|
return;
|
|
|
|
total_pages = PHYS_PFN(memblock_phys_mem_size());
|
|
if (csv_mem_size) {
|
|
if (csv_mem_size < (total_pages << PAGE_SHIFT)) {
|
|
csv_mem_percentage = csv_mem_size * 100 / (total_pages << PAGE_SHIFT);
|
|
if (csv_mem_percentage > 80)
|
|
csv_mem_percentage = 80; /* Maximum percentage */
|
|
} else
|
|
csv_mem_percentage = 80; /* Maximum percentage */
|
|
}
|
|
|
|
if (!csv_mem_percentage) {
|
|
pr_warn("Don't reserve any memory\n");
|
|
return;
|
|
}
|
|
|
|
csv_cma_reserve_mem();
|
|
}
|
|
|
|
static void enable_used_hugtlb_migration(void)
|
|
{
|
|
spin_lock(&control.lock);
|
|
if (!control.enabled_counts) {
|
|
control.last_value = sysctl_enable_used_hugtlb_migration;
|
|
sysctl_enable_used_hugtlb_migration = 1;
|
|
}
|
|
control.enabled_counts++;
|
|
spin_unlock(&control.lock);
|
|
}
|
|
|
|
static void disable_used_hugtlb_migration(void)
|
|
{
|
|
spin_lock(&control.lock);
|
|
control.enabled_counts--;
|
|
if (!control.enabled_counts)
|
|
sysctl_enable_used_hugtlb_migration = control.last_value;
|
|
spin_unlock(&control.lock);
|
|
}
|
|
|
|
phys_addr_t csv_alloc_from_contiguous(size_t size, nodemask_t *nodes_allowed,
|
|
unsigned int align)
|
|
{
|
|
int nid;
|
|
int nr_nodes;
|
|
struct page *page = NULL;
|
|
phys_addr_t phys_addr;
|
|
int count;
|
|
struct csv_cma *csv_cma;
|
|
int fast = 1;
|
|
|
|
if (!nodes_allowed || size > CSV_CMA_SIZE) {
|
|
pr_err("Invalid params, size = 0x%lx, nodes_allowed = %p\n",
|
|
size, nodes_allowed);
|
|
return 0;
|
|
}
|
|
|
|
align = min_t(unsigned int, align, get_order(CSV_CMA_SIZE));
|
|
retry:
|
|
nr_nodes = nodes_weight(*nodes_allowed);
|
|
|
|
/* Traverse from current node */
|
|
nid = numa_node_id();
|
|
if (!node_isset(nid, *nodes_allowed))
|
|
nid = next_node_in(nid, *nodes_allowed);
|
|
|
|
for (; nr_nodes > 0; nid = next_node_in(nid, *nodes_allowed), nr_nodes--) {
|
|
struct cma_array *array = csv_contiguous_pernuma_area[nid];
|
|
|
|
if (!array)
|
|
continue;
|
|
|
|
count = array->count;
|
|
while (count) {
|
|
csv_cma = &array->csv_cma[count - 1];
|
|
|
|
/*
|
|
* The value check of csv_cma->fast is lockless, but
|
|
* that's ok as this don't affect functional correntness
|
|
* whatever the value of csv_cma->fast.
|
|
*/
|
|
if (fast && !csv_cma->fast) {
|
|
count--;
|
|
continue;
|
|
}
|
|
enable_used_hugtlb_migration();
|
|
page = cma_alloc(csv_cma->cma, PAGE_ALIGN(size) >> PAGE_SHIFT,
|
|
align, true);
|
|
disable_used_hugtlb_migration();
|
|
if (page) {
|
|
page->private = (unsigned long)csv_cma;
|
|
if (!csv_cma->fast)
|
|
csv_cma->fast = 1;
|
|
goto success;
|
|
} else
|
|
csv_cma->fast = 0;
|
|
|
|
count--;
|
|
}
|
|
}
|
|
|
|
if (fast) {
|
|
fast = 0;
|
|
goto retry;
|
|
} else {
|
|
pr_err("Fail to alloc secure memory(size = 0x%lx)\n", size);
|
|
return 0;
|
|
}
|
|
|
|
success:
|
|
phys_addr = page_to_phys(page);
|
|
clflush_cache_range(__va(phys_addr), size);
|
|
|
|
return phys_addr;
|
|
}
|
|
EXPORT_SYMBOL_GPL(csv_alloc_from_contiguous);
|
|
|
|
void csv_release_to_contiguous(phys_addr_t pa, size_t size)
|
|
{
|
|
struct csv_cma *csv_cma;
|
|
struct page *page = pfn_to_page(pa >> PAGE_SHIFT);
|
|
|
|
WARN_ON(!page);
|
|
if (likely(page)) {
|
|
csv_cma = (struct csv_cma *)page->private;
|
|
WARN_ON(!csv_cma);
|
|
if (likely(csv_cma)) {
|
|
page->private = 0;
|
|
csv_cma->fast = 1;
|
|
cma_release(csv_cma->cma, page, PAGE_ALIGN(size) >> PAGE_SHIFT);
|
|
}
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(csv_release_to_contiguous);
|
|
|
|
static int __init csv_mm_init(void)
|
|
{
|
|
spin_lock_init(&control.lock);
|
|
control.enabled_counts = 0;
|
|
return 0;
|
|
}
|
|
|
|
static void __exit csv_mm_exit(void)
|
|
{
|
|
}
|
|
|
|
module_init(csv_mm_init);
|
|
module_exit(csv_mm_exit);
|