ia64/xen-unstable

view xen/arch/x86/hvm/viridian.c @ 18637:8c3144b1ff63

x86: Fix non-debug build.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Oct 15 08:22:42 2008 +0100 (2008-10-15)
parents 8d993552673a
children 6674835e18e7
line source
1 /******************************************************************************
2 * viridian.c
3 *
4 * An implementation of the Viridian hypercall interface.
5 */
7 #include <xen/sched.h>
8 #include <xen/version.h>
9 #include <xen/perfc.h>
10 #include <xen/hypercall.h>
11 #include <xen/domain_page.h>
12 #include <asm/paging.h>
13 #include <asm/p2m.h>
14 #include <asm/hvm/support.h>
15 #include <public/sched.h>
16 #include <public/hvm/hvm_op.h>
18 /* Viridian MSR numbers. */
19 #define VIRIDIAN_MSR_GUEST_OS_ID 0x40000000
20 #define VIRIDIAN_MSR_HYPERCALL 0x40000001
21 #define VIRIDIAN_MSR_VP_INDEX 0x40000002
22 #define VIRIDIAN_MSR_EOI 0x40000070
23 #define VIRIDIAN_MSR_ICR 0x40000071
24 #define VIRIDIAN_MSR_TPR 0x40000072
26 /* Viridian Hypercall Status Codes. */
27 #define HV_STATUS_SUCCESS 0x0000
28 #define HV_STATUS_INVALID_HYPERCALL_CODE 0x0002
30 /* Viridian Hypercall Codes and Parameters. */
31 #define HvNotifyLongSpinWait 8
33 /* Viridian CPUID 4000003, Viridian MSR availability. */
34 #define CPUID3A_MSR_APIC_ACCESS (1 << 4)
35 #define CPUID3A_MSR_HYPERCALL (1 << 5)
36 #define CPUID3A_MSR_VP_INDEX (1 << 6)
38 /* Viridian CPUID 4000004, Implementation Recommendations. */
39 #define CPUID4A_MSR_BASED_APIC (1 << 3)
41 int cpuid_viridian_leaves(unsigned int leaf, unsigned int *eax,
42 unsigned int *ebx, unsigned int *ecx,
43 unsigned int *edx)
44 {
45 struct domain *d = current->domain;
47 if ( !is_viridian_domain(d) )
48 return 0;
50 leaf -= 0x40000000;
51 if ( leaf > 5 )
52 return 0;
54 *eax = *ebx = *ecx = *edx = 0;
55 switch ( leaf )
56 {
57 case 0:
58 *eax = 0x40000005; /* Maximum leaf */
59 *ebx = 0x7263694d; /* Magic numbers */
60 *ecx = 0x666F736F;
61 *edx = 0x76482074;
62 break;
63 case 1:
64 *eax = 0x31237648; /* Version number */
65 break;
66 case 2:
67 /* Hypervisor information, but only if the guest has set its
68 own version number. */
69 if ( d->arch.hvm_domain.viridian.guest_os_id.raw == 0 )
70 break;
71 *eax = 1; /* Build number */
72 *ebx = (xen_major_version() << 16) | xen_minor_version();
73 *ecx = 0; /* SP */
74 *edx = 0; /* Service branch and number */
75 break;
76 case 3:
77 /* Which hypervisor MSRs are available to the guest */
78 *eax = (CPUID3A_MSR_APIC_ACCESS |
79 CPUID3A_MSR_HYPERCALL |
80 CPUID3A_MSR_VP_INDEX);
81 break;
82 case 4:
83 /* Recommended hypercall usage. */
84 if ( (d->arch.hvm_domain.viridian.guest_os_id.raw == 0) ||
85 (d->arch.hvm_domain.viridian.guest_os_id.fields.os < 4) )
86 break;
87 *eax = CPUID4A_MSR_BASED_APIC;
88 *ebx = 2047; /* long spin count */
89 break;
90 }
92 return 1;
93 }
95 static void enable_hypercall_page(void)
96 {
97 struct domain *d = current->domain;
98 unsigned long gmfn = d->arch.hvm_domain.viridian.hypercall_gpa.fields.pfn;
99 unsigned long mfn = gmfn_to_mfn(d, gmfn);
100 uint8_t *p;
102 if ( !mfn_valid(mfn) ||
103 !get_page_and_type(mfn_to_page(mfn), d, PGT_writable_page) )
104 {
105 gdprintk(XENLOG_WARNING, "Bad GMFN %lx (MFN %lx)\n", gmfn, mfn);
106 return;
107 }
109 p = map_domain_page(mfn);
111 /*
112 * We set the bit 31 in %eax (reserved field in the Viridian hypercall
113 * calling convention) to differentiate Xen and Viridian hypercalls.
114 */
115 *(u8 *)(p + 0) = 0x0d; /* orl $0x80000000, %eax */
116 *(u32 *)(p + 1) = 0x80000000;
117 *(u8 *)(p + 5) = 0x0f; /* vmcall/vmmcall */
118 *(u8 *)(p + 6) = 0x01;
119 *(u8 *)(p + 7) = ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
120 ? 0xc1 : 0xd9);
121 *(u8 *)(p + 8) = 0xc3; /* ret */
122 memset(p + 9, 0xcc, PAGE_SIZE - 9); /* int3, int3, ... */
124 unmap_domain_page(p);
126 put_page_and_type(mfn_to_page(mfn));
127 }
129 int wrmsr_viridian_regs(uint32_t idx, uint32_t eax, uint32_t edx)
130 {
131 struct domain *d = current->domain;
132 uint64_t val = ((uint64_t)edx << 32) | eax;
134 if ( !is_viridian_domain(d) )
135 return 0;
137 switch ( idx )
138 {
139 case VIRIDIAN_MSR_GUEST_OS_ID:
140 perfc_incr(mshv_wrmsr_osid);
141 d->arch.hvm_domain.viridian.guest_os_id.raw = val;
142 gdprintk(XENLOG_INFO, "Guest os:\n");
143 gdprintk(XENLOG_INFO, "\tvendor: %x\n",
144 d->arch.hvm_domain.viridian.guest_os_id.fields.vendor);
145 gdprintk(XENLOG_INFO, "\tos: %x\n",
146 d->arch.hvm_domain.viridian.guest_os_id.fields.os);
147 gdprintk(XENLOG_INFO, "\tmajor: %x\n",
148 d->arch.hvm_domain.viridian.guest_os_id.fields.major);
149 gdprintk(XENLOG_INFO, "\tminor: %x\n",
150 d->arch.hvm_domain.viridian.guest_os_id.fields.minor);
151 gdprintk(XENLOG_INFO, "\tsp: %x\n",
152 d->arch.hvm_domain.viridian.guest_os_id.fields.service_pack);
153 gdprintk(XENLOG_INFO, "\tbuild: %x\n",
154 d->arch.hvm_domain.viridian.guest_os_id.fields.build_number);
155 break;
157 case VIRIDIAN_MSR_HYPERCALL:
158 perfc_incr(mshv_wrmsr_hc_page);
159 gdprintk(XENLOG_INFO, "Set hypercall page %"PRIx64".\n", val);
160 if ( d->arch.hvm_domain.viridian.guest_os_id.raw == 0 )
161 break;
162 d->arch.hvm_domain.viridian.hypercall_gpa.raw = val;
163 if ( d->arch.hvm_domain.viridian.hypercall_gpa.fields.enabled )
164 enable_hypercall_page();
165 break;
167 case VIRIDIAN_MSR_VP_INDEX:
168 perfc_incr(mshv_wrmsr_vp_index);
169 gdprintk(XENLOG_INFO, "Set VP index %"PRIu64".\n", val);
170 break;
172 case VIRIDIAN_MSR_EOI:
173 perfc_incr(mshv_wrmsr_eoi);
174 vlapic_EOI_set(vcpu_vlapic(current));
175 break;
177 case VIRIDIAN_MSR_ICR: {
178 struct vlapic *vlapic = vcpu_vlapic(current);
179 perfc_incr(mshv_wrmsr_icr);
180 eax &= ~(1 << 12);
181 edx &= 0xff000000;
182 vlapic_set_reg(vlapic, APIC_ICR2, edx);
183 if ( vlapic_ipi(vlapic, eax, edx) == X86EMUL_OKAY )
184 vlapic_set_reg(vlapic, APIC_ICR, eax);
185 break;
186 }
188 case VIRIDIAN_MSR_TPR:
189 perfc_incr(mshv_wrmsr_tpr);
190 vlapic_set_reg(vcpu_vlapic(current), APIC_TASKPRI, eax & 0xff);
191 break;
193 default:
194 return 0;
195 }
197 return 1;
198 }
200 int rdmsr_viridian_regs(uint32_t idx, uint32_t *eax, uint32_t *edx)
201 {
202 uint64_t val;
203 struct vcpu *v = current;
205 if ( !is_viridian_domain(v->domain) )
206 return 0;
208 switch ( idx )
209 {
210 case VIRIDIAN_MSR_GUEST_OS_ID:
211 perfc_incr(mshv_rdmsr_osid);
212 val = v->domain->arch.hvm_domain.viridian.guest_os_id.raw;
213 break;
215 case VIRIDIAN_MSR_HYPERCALL:
216 perfc_incr(mshv_rdmsr_hc_page);
217 val = v->domain->arch.hvm_domain.viridian.hypercall_gpa.raw;
218 break;
220 case VIRIDIAN_MSR_VP_INDEX:
221 perfc_incr(mshv_rdmsr_vp_index);
222 val = v->vcpu_id;
223 break;
225 case VIRIDIAN_MSR_ICR:
226 perfc_incr(mshv_rdmsr_icr);
227 val = (((uint64_t)vlapic_get_reg(vcpu_vlapic(v), APIC_ICR2) << 32) |
228 vlapic_get_reg(vcpu_vlapic(v), APIC_ICR));
229 break;
231 case VIRIDIAN_MSR_TPR:
232 perfc_incr(mshv_rdmsr_tpr);
233 val = vlapic_get_reg(vcpu_vlapic(v), APIC_TASKPRI);
234 break;
236 default:
237 return 0;
238 }
240 *eax = val;
241 *edx = val >> 32;
242 return 1;
243 }
245 int viridian_hypercall(struct cpu_user_regs *regs)
246 {
247 int mode = hvm_guest_x86_mode(current);
248 unsigned long input_params_gpa, output_params_gpa;
249 uint16_t status = HV_STATUS_SUCCESS;
251 union hypercall_input {
252 uint64_t raw;
253 struct {
254 uint16_t call_code;
255 uint16_t rsvd1;
256 unsigned rep_count:12;
257 unsigned rsvd2:4;
258 unsigned rep_start:12;
259 unsigned rsvd3:4;
260 };
261 } input;
263 union hypercall_output {
264 uint64_t raw;
265 struct {
266 uint16_t result;
267 uint16_t rsvd1;
268 unsigned rep_complete:12;
269 unsigned rsvd2:20;
270 };
271 } output = { 0 };
273 ASSERT(is_viridian_domain(current->domain));
275 switch ( mode )
276 {
277 #ifdef __x86_64__
278 case 8:
279 input.raw = regs->rcx;
280 input_params_gpa = regs->rdx;
281 output_params_gpa = regs->r8;
282 break;
283 #endif
284 case 4:
285 input.raw = ((uint64_t)regs->edx << 32) | regs->eax;
286 input_params_gpa = ((uint64_t)regs->ebx << 32) | regs->ecx;
287 output_params_gpa = ((uint64_t)regs->edi << 32) | regs->esi;
288 break;
289 default:
290 goto out;
291 }
293 switch ( input.call_code )
294 {
295 case HvNotifyLongSpinWait:
296 perfc_incr(mshv_call_long_wait);
297 do_sched_op_compat(SCHEDOP_yield, 0);
298 status = HV_STATUS_SUCCESS;
299 break;
300 default:
301 status = HV_STATUS_INVALID_HYPERCALL_CODE;
302 break;
303 }
305 out:
306 output.result = status;
307 switch (mode) {
308 #ifdef __x86_64__
309 case 8:
310 regs->rax = output.raw;
311 break;
312 #endif
313 default:
314 regs->edx = output.raw >> 32;
315 regs->eax = output.raw;
316 break;
317 }
319 return HVM_HCALL_completed;
320 }
322 static int viridian_save_cpu_ctxt(struct domain *d, hvm_domain_context_t *h)
323 {
324 struct hvm_viridian_context ctxt;
326 if ( !is_viridian_domain(d) )
327 return 0;
329 ctxt.hypercall_gpa = d->arch.hvm_domain.viridian.hypercall_gpa.raw;
330 ctxt.guest_os_id = d->arch.hvm_domain.viridian.guest_os_id.raw;
332 return (hvm_save_entry(VIRIDIAN, 0, h, &ctxt) != 0);
333 }
335 static int viridian_load_cpu_ctxt(struct domain *d, hvm_domain_context_t *h)
336 {
337 struct hvm_viridian_context ctxt;
339 if ( hvm_load_entry(VIRIDIAN, h, &ctxt) != 0 )
340 return -EINVAL;
342 d->arch.hvm_domain.viridian.hypercall_gpa.raw = ctxt.hypercall_gpa;
343 d->arch.hvm_domain.viridian.guest_os_id.raw = ctxt.guest_os_id;
345 return 0;
346 }
348 HVM_REGISTER_SAVE_RESTORE(VIRIDIAN, viridian_save_cpu_ctxt,
349 viridian_load_cpu_ctxt, 1, HVMSR_PER_DOM);