summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kvm/book3s_hv_builtin.c
blob: a795a13f4a70f9ddc212e8d125857ad50232248a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 * Copyright 2011 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation.
 */

#include <linux/kvm_host.h>
#include <linux/preempt.h>
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/bootmem.h>
#include <linux/init.h>

#include <asm/cputable.h>
#include <asm/kvm_ppc.h>
#include <asm/kvm_book3s.h>

/*
 * This maintains a list of RMAs (real mode areas) for KVM guests to use.
 * Each RMA has to be physically contiguous and of a size that the
 * hardware supports.  PPC970 and POWER7 support 64MB, 128MB and 256MB,
 * and other larger sizes.  Since we are unlikely to be allocate that
 * much physically contiguous memory after the system is up and running,
 * we preallocate a set of RMAs in early boot for KVM to use.
 */
static unsigned long kvm_rma_size = 64 << 20;	/* 64MB */
static unsigned long kvm_rma_count;

static int __init early_parse_rma_size(char *p)
{
	if (!p)
		return 1;

	kvm_rma_size = memparse(p, &p);

	return 0;
}
early_param("kvm_rma_size", early_parse_rma_size);

static int __init early_parse_rma_count(char *p)
{
	if (!p)
		return 1;

	kvm_rma_count = simple_strtoul(p, NULL, 0);

	return 0;
}
early_param("kvm_rma_count", early_parse_rma_count);

static struct kvmppc_rma_info *rma_info;
static LIST_HEAD(free_rmas);
static DEFINE_SPINLOCK(rma_lock);

/* Work out RMLS (real mode limit selector) field value for a given RMA size.
   Assumes POWER7 or PPC970. */
static inline int lpcr_rmls(unsigned long rma_size)
{
	switch (rma_size) {
	case 32ul << 20:	/* 32 MB */
		if (cpu_has_feature(CPU_FTR_ARCH_206))
			return 8;	/* only supported on POWER7 */
		return -1;
	case 64ul << 20:	/* 64 MB */
		return 3;
	case 128ul << 20:	/* 128 MB */
		return 7;
	case 256ul << 20:	/* 256 MB */
		return 4;
	case 1ul << 30:		/* 1 GB */
		return 2;
	case 16ul << 30:	/* 16 GB */
		return 1;
	case 256ul << 30:	/* 256 GB */
		return 0;
	default:
		return -1;
	}
}

/*
 * Called at boot time while the bootmem allocator is active,
 * to allocate contiguous physical memory for the real memory
 * areas for guests.
 */
void __init kvm_rma_init(void)
{
	unsigned long i;
	unsigned long j, npages;
	void *rma;
	struct page *pg;

	/* Only do this on PPC970 in HV mode */
	if (!cpu_has_feature(CPU_FTR_HVMODE) ||
	    !cpu_has_feature(CPU_FTR_ARCH_201))
		return;

	if (!kvm_rma_size || !kvm_rma_count)
		return;

	/* Check that the requested size is one supported in hardware */
	if (lpcr_rmls(kvm_rma_size) < 0) {
		pr_err("RMA size of 0x%lx not supported\n", kvm_rma_size);
		return;
	}

	npages = kvm_rma_size >> PAGE_SHIFT;
	rma_info = alloc_bootmem(kvm_rma_count * sizeof(struct kvmppc_rma_info));
	for (i = 0; i < kvm_rma_count; ++i) {
		rma = alloc_bootmem_align(kvm_rma_size, kvm_rma_size);
		pr_info("Allocated KVM RMA at %p (%ld MB)\n", rma,
			kvm_rma_size >> 20);
		rma_info[i].base_virt = rma;
		rma_info[i].base_pfn = __pa(rma) >> PAGE_SHIFT;
		rma_info[i].npages = npages;
		list_add_tail(&rma_info[i].list, &free_rmas);
		atomic_set(&rma_info[i].use_count, 0);

		pg = pfn_to_page(rma_info[i].base_pfn);
		for (j = 0; j < npages; ++j) {
			atomic_inc(&pg->_count);
			++pg;
		}
	}
}

struct kvmppc_rma_info *kvm_alloc_rma(void)
{
	struct kvmppc_rma_info *ri;

	ri = NULL;
	spin_lock(&rma_lock);
	if (!list_empty(&free_rmas)) {
		ri = list_first_entry(&free_rmas, struct kvmppc_rma_info, list);
		list_del(&ri->list);
		atomic_inc(&ri->use_count);
	}
	spin_unlock(&rma_lock);
	return ri;
}
EXPORT_SYMBOL_GPL(kvm_alloc_rma);

void kvm_release_rma(struct kvmppc_rma_info *ri)
{
	if (atomic_dec_and_test(&ri->use_count)) {
		spin_lock(&rma_lock);
		list_add_tail(&ri->list, &free_rmas);
		spin_unlock(&rma_lock);

	}
}
EXPORT_SYMBOL_GPL(kvm_release_rma);

OpenPOWER on IntegriCloud