blob: 63fed6074f32cc6adce782b0544c524ad8ddc244 [file] [log] [blame]
Oleksandr Andrushchenko48654412020-08-06 12:42:48 +03001// SPDX-License-Identifier: MIT License
2/*
3 * hypervisor.c
4 *
5 * Communication to/from hypervisor.
6 *
7 * Copyright (c) 2002-2003, K A Fraser
8 * Copyright (c) 2005, Grzegorz Milos, gm281@cam.ac.uk,Intel Research Cambridge
9 * Copyright (c) 2020, EPAM Systems Inc.
10 */
11#include <common.h>
12#include <cpu_func.h>
13#include <log.h>
14#include <memalign.h>
15
16#include <asm/io.h>
17#include <asm/armv8/mmu.h>
18#include <asm/xen/system.h>
19
20#include <linux/bug.h>
21
22#include <xen/hvm.h>
Oleksandr Andrushchenko673fd822020-08-06 12:42:49 +030023#include <xen/events.h>
Oleksandr Andrushchenko48654412020-08-06 12:42:48 +030024#include <xen/interface/memory.h>
25
26#define active_evtchns(cpu, sh, idx) \
27 ((sh)->evtchn_pending[idx] & \
28 ~(sh)->evtchn_mask[idx])
29
30int in_callback;
31
32/*
33 * Shared page for communicating with the hypervisor.
34 * Events flags go here, for example.
35 */
36struct shared_info *HYPERVISOR_shared_info;
37
38static const char *param_name(int op)
39{
40#define PARAM(x)[HVM_PARAM_##x] = #x
41 static const char *const names[] = {
42 PARAM(CALLBACK_IRQ),
43 PARAM(STORE_PFN),
44 PARAM(STORE_EVTCHN),
45 PARAM(PAE_ENABLED),
46 PARAM(IOREQ_PFN),
47 PARAM(VPT_ALIGN),
48 PARAM(CONSOLE_PFN),
49 PARAM(CONSOLE_EVTCHN),
50 };
51#undef PARAM
52
53 if (op >= ARRAY_SIZE(names))
54 return "unknown";
55
56 if (!names[op])
57 return "reserved";
58
59 return names[op];
60}
61
62/**
63 * hvm_get_parameter_maintain_dcache - function to obtain a HVM
64 * parameter value.
65 * @idx: HVM parameter index
66 * @value: Value to fill in
67 *
68 * According to Xen on ARM ABI (xen/include/public/arch-arm.h):
69 * all memory which is shared with other entities in the system
70 * (including the hypervisor and other guests) must reside in memory
71 * which is mapped as Normal Inner Write-Back Outer Write-Back
72 * Inner-Shareable.
73 *
74 * Thus, page attributes must be equally set for all the entities
75 * working with that page.
76 *
77 * Before MMU setup the data cache is turned off, so it means that
78 * manual data cache maintenance is required, because of the
79 * difference of page attributes.
80 */
81int hvm_get_parameter_maintain_dcache(int idx, uint64_t *value)
82{
83 struct xen_hvm_param xhv;
84 int ret;
85
86 invalidate_dcache_range((unsigned long)&xhv,
87 (unsigned long)&xhv + sizeof(xhv));
88 xhv.domid = DOMID_SELF;
89 xhv.index = idx;
90 invalidate_dcache_range((unsigned long)&xhv,
91 (unsigned long)&xhv + sizeof(xhv));
92
93 ret = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv);
94 if (ret < 0) {
95 pr_err("Cannot get hvm parameter %s (%d): %d!\n",
96 param_name(idx), idx, ret);
97 BUG();
98 }
99 invalidate_dcache_range((unsigned long)&xhv,
100 (unsigned long)&xhv + sizeof(xhv));
101
102 *value = xhv.value;
103
104 return ret;
105}
106
107int hvm_get_parameter(int idx, uint64_t *value)
108{
109 struct xen_hvm_param xhv;
110 int ret;
111
112 xhv.domid = DOMID_SELF;
113 xhv.index = idx;
114 ret = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv);
115 if (ret < 0) {
116 pr_err("Cannot get hvm parameter %s (%d): %d!\n",
117 param_name(idx), idx, ret);
118 BUG();
119 }
120
121 *value = xhv.value;
122
123 return ret;
124}
125
126struct shared_info *map_shared_info(void *p)
127{
128 struct xen_add_to_physmap xatp;
129
130 HYPERVISOR_shared_info = (struct shared_info *)memalign(PAGE_SIZE,
131 PAGE_SIZE);
132 if (!HYPERVISOR_shared_info)
133 BUG();
134
135 xatp.domid = DOMID_SELF;
136 xatp.idx = 0;
137 xatp.space = XENMAPSPACE_shared_info;
138 xatp.gpfn = virt_to_pfn(HYPERVISOR_shared_info);
139 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp) != 0)
140 BUG();
141
142 return HYPERVISOR_shared_info;
143}
144
145void do_hypervisor_callback(struct pt_regs *regs)
146{
147 unsigned long l1, l2, l1i, l2i;
148 unsigned int port;
149 int cpu = 0;
150 struct shared_info *s = HYPERVISOR_shared_info;
151 struct vcpu_info *vcpu_info = &s->vcpu_info[cpu];
152
153 in_callback = 1;
154
155 vcpu_info->evtchn_upcall_pending = 0;
156 l1 = xchg(&vcpu_info->evtchn_pending_sel, 0);
157
158 while (l1 != 0) {
159 l1i = __ffs(l1);
160 l1 &= ~(1UL << l1i);
161
162 while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {
163 l2i = __ffs(l2);
164 l2 &= ~(1UL << l2i);
165
166 port = (l1i * (sizeof(unsigned long) * 8)) + l2i;
Oleksandr Andrushchenko673fd822020-08-06 12:42:49 +0300167 do_event(port, regs);
Oleksandr Andrushchenko48654412020-08-06 12:42:48 +0300168 }
169 }
170
171 in_callback = 0;
172}
173
174void force_evtchn_callback(void)
175{
176#ifdef XEN_HAVE_PV_UPCALL_MASK
177 int save;
178#endif
179 struct vcpu_info *vcpu;
180
181 vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()];
182#ifdef XEN_HAVE_PV_UPCALL_MASK
183 save = vcpu->evtchn_upcall_mask;
184#endif
185
186 while (vcpu->evtchn_upcall_pending) {
187#ifdef XEN_HAVE_PV_UPCALL_MASK
188 vcpu->evtchn_upcall_mask = 1;
189#endif
190 do_hypervisor_callback(NULL);
191#ifdef XEN_HAVE_PV_UPCALL_MASK
192 vcpu->evtchn_upcall_mask = save;
193#endif
194 };
195}
196
197void mask_evtchn(uint32_t port)
198{
199 struct shared_info *s = HYPERVISOR_shared_info;
200
201 synch_set_bit(port, &s->evtchn_mask[0]);
202}
203
204void unmask_evtchn(uint32_t port)
205{
206 struct shared_info *s = HYPERVISOR_shared_info;
207 struct vcpu_info *vcpu_info = &s->vcpu_info[smp_processor_id()];
208
209 synch_clear_bit(port, &s->evtchn_mask[0]);
210
211 /*
212 * Just like a real IO-APIC we 'lose the interrupt edge' if the
213 * channel is masked.
214 */
215 if (synch_test_bit(port, &s->evtchn_pending[0]) &&
216 !synch_test_and_set_bit(port / (sizeof(unsigned long) * 8),
217 &vcpu_info->evtchn_pending_sel)) {
218 vcpu_info->evtchn_upcall_pending = 1;
219#ifdef XEN_HAVE_PV_UPCALL_MASK
220 if (!vcpu_info->evtchn_upcall_mask)
221#endif
222 force_evtchn_callback();
223 }
224}
225
226void clear_evtchn(uint32_t port)
227{
228 struct shared_info *s = HYPERVISOR_shared_info;
229
230 synch_clear_bit(port, &s->evtchn_pending[0]);
231}
232
233void xen_init(void)
234{
235 debug("%s\n", __func__);
236
237 map_shared_info(NULL);
Oleksandr Andrushchenko673fd822020-08-06 12:42:49 +0300238 init_events();
Oleksandr Andrushchenko48654412020-08-06 12:42:48 +0300239}
240