summaryrefslogtreecommitdiff
path: root/arch/s390/kvm/gmap.c
blob: 02adf151d4de4cf74cf22ce9b9f7d110bbd6da3e (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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// SPDX-License-Identifier: GPL-2.0
/*
 * Guest memory management for KVM/s390
 *
 * Copyright IBM Corp. 2008, 2020, 2024
 *
 *    Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com>
 *               Martin Schwidefsky <schwidefsky@de.ibm.com>
 *               David Hildenbrand <david@redhat.com>
 *               Janosch Frank <frankja@linux.vnet.ibm.com>
 */

#include <linux/compiler.h>
#include <linux/kvm.h>
#include <linux/kvm_host.h>
#include <linux/pgtable.h>
#include <linux/pagemap.h>

#include <asm/lowcore.h>
#include <asm/gmap.h>
#include <asm/uv.h>

#include "gmap.h"

/**
 * should_export_before_import - Determine whether an export is needed
 * before an import-like operation
 * @uvcb: the Ultravisor control block of the UVC to be performed
 * @mm: the mm of the process
 *
 * Returns whether an export is needed before every import-like operation.
 * This is needed for shared pages, which don't trigger a secure storage
 * exception when accessed from a different guest.
 *
 * Although considered as one, the Unpin Page UVC is not an actual import,
 * so it is not affected.
 *
 * No export is needed also when there is only one protected VM, because the
 * page cannot belong to the wrong VM in that case (there is no "other VM"
 * it can belong to).
 *
 * Return: true if an export is needed before every import, otherwise false.
 */
static bool should_export_before_import(struct uv_cb_header *uvcb, struct mm_struct *mm)
{
	/*
	 * The misc feature indicates, among other things, that importing a
	 * shared page from a different protected VM will automatically also
	 * transfer its ownership.
	 */
	if (uv_has_feature(BIT_UV_FEAT_MISC))
		return false;
	if (uvcb->cmd == UVC_CMD_UNPIN_PAGE_SHARED)
		return false;
	return atomic_read(&mm->context.protected_count) > 1;
}

static int __gmap_make_secure(struct gmap *gmap, struct page *page, void *uvcb)
{
	struct folio *folio = page_folio(page);
	int rc;

	/*
	 * Secure pages cannot be huge and userspace should not combine both.
	 * In case userspace does it anyway this will result in an -EFAULT for
	 * the unpack. The guest is thus never reaching secure mode.
	 * If userspace plays dirty tricks and decides to map huge pages at a
	 * later point in time, it will receive a segmentation fault or
	 * KVM_RUN will return -EFAULT.
	 */
	if (folio_test_hugetlb(folio))
		return -EFAULT;
	if (folio_test_large(folio)) {
		mmap_read_unlock(gmap->mm);
		rc = kvm_s390_wiggle_split_folio(gmap->mm, folio, true);
		mmap_read_lock(gmap->mm);
		if (rc)
			return rc;
		folio = page_folio(page);
	}

	if (!folio_trylock(folio))
		return -EAGAIN;
	if (should_export_before_import(uvcb, gmap->mm))
		uv_convert_from_secure(folio_to_phys(folio));
	rc = make_folio_secure(folio, uvcb);
	folio_unlock(folio);

	/*
	 * In theory a race is possible and the folio might have become
	 * large again before the folio_trylock() above. In that case, no
	 * action is performed and -EAGAIN is returned; the callers will
	 * have to try again later.
	 * In most cases this implies running the VM again, getting the same
	 * exception again, and make another attempt in this function.
	 * This is expected to happen extremely rarely.
	 */
	if (rc == -E2BIG)
		return -EAGAIN;
	/* The folio has too many references, try to shake some off */
	if (rc == -EBUSY) {
		mmap_read_unlock(gmap->mm);
		kvm_s390_wiggle_split_folio(gmap->mm, folio, false);
		mmap_read_lock(gmap->mm);
		return -EAGAIN;
	}

	return rc;
}

/**
 * gmap_make_secure() - make one guest page secure
 * @gmap: the guest gmap
 * @gaddr: the guest address that needs to be made secure
 * @uvcb: the UVCB specifying which operation needs to be performed
 *
 * Context: needs to be called with kvm->srcu held.
 * Return: 0 on success, < 0 in case of error (see __gmap_make_secure()).
 */
int gmap_make_secure(struct gmap *gmap, unsigned long gaddr, void *uvcb)
{
	struct kvm *kvm = gmap->private;
	struct page *page;
	int rc = 0;

	lockdep_assert_held(&kvm->srcu);

	page = gfn_to_page(kvm, gpa_to_gfn(gaddr));
	mmap_read_lock(gmap->mm);
	if (page)
		rc = __gmap_make_secure(gmap, page, uvcb);
	kvm_release_page_clean(page);
	mmap_read_unlock(gmap->mm);

	return rc;
}

int gmap_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
{
	struct uv_cb_cts uvcb = {
		.header.cmd = UVC_CMD_CONV_TO_SEC_STOR,
		.header.len = sizeof(uvcb),
		.guest_handle = gmap->guest_handle,
		.gaddr = gaddr,
	};

	return gmap_make_secure(gmap, gaddr, &uvcb);
}

/**
 * __gmap_destroy_page() - Destroy a guest page.
 * @gmap: the gmap of the guest
 * @page: the page to destroy
 *
 * An attempt will be made to destroy the given guest page. If the attempt
 * fails, an attempt is made to export the page. If both attempts fail, an
 * appropriate error is returned.
 *
 * Context: must be called holding the mm lock for gmap->mm
 */
static int __gmap_destroy_page(struct gmap *gmap, struct page *page)
{
	struct folio *folio = page_folio(page);
	int rc;

	/*
	 * See gmap_make_secure(): large folios cannot be secure. Small
	 * folio implies FW_LEVEL_PTE.
	 */
	if (folio_test_large(folio))
		return -EFAULT;

	rc = uv_destroy_folio(folio);
	/*
	 * Fault handlers can race; it is possible that two CPUs will fault
	 * on the same secure page. One CPU can destroy the page, reboot,
	 * re-enter secure mode and import it, while the second CPU was
	 * stuck at the beginning of the handler. At some point the second
	 * CPU will be able to progress, and it will not be able to destroy
	 * the page. In that case we do not want to terminate the process,
	 * we instead try to export the page.
	 */
	if (rc)
		rc = uv_convert_from_secure_folio(folio);

	return rc;
}

/**
 * gmap_destroy_page() - Destroy a guest page.
 * @gmap: the gmap of the guest
 * @gaddr: the guest address to destroy
 *
 * An attempt will be made to destroy the given guest page. If the attempt
 * fails, an attempt is made to export the page. If both attempts fail, an
 * appropriate error is returned.
 *
 * Context: may sleep.
 */
int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr)
{
	struct page *page;
	int rc = 0;

	mmap_read_lock(gmap->mm);
	page = gfn_to_page(gmap->private, gpa_to_gfn(gaddr));
	if (page)
		rc = __gmap_destroy_page(gmap, page);
	kvm_release_page_clean(page);
	mmap_read_unlock(gmap->mm);
	return rc;
}