1/*
2 * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7/* This file contains macros for CPUID emulation in x86.
8 * Most of the code in this file is from arch/x86/kvm/cpuid.h Linux 3.8.8
9
10 *     Authors:
11 *         Qian Ge
12 */
13
14#include <stdio.h>
15#include <stdlib.h>
16
17#include <sel4/sel4.h>
18
19#include <sel4vm/guest_vm.h>
20#include <sel4vm/arch/guest_x86_context.h>
21
22#include "processor/cpuid.h"
23#include "processor/cpufeature.h"
24
25#include "vm.h"
26#include "guest_state.h"
27
28static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
29                                unsigned int *ecx, unsigned int *edx)
30{
31    /* ecx is often an input as well as an output. */
32    asm volatile("cpuid"
33                 : "=a"(*eax),
34                 "=b"(*ebx),
35                 "=c"(*ecx),
36                 "=d"(*edx)
37                 : "0"(*eax), "2"(*ecx)
38                 : "memory");
39}
40
41static int vm_cpuid_virt(unsigned int function, unsigned int index, struct cpuid_val *val, vm_vcpu_t *vcpu)
42{
43    unsigned int eax, ebx, ecx, edx;
44
45    eax = function;
46    ecx = index;
47
48    native_cpuid(&eax, &ebx, &ecx, &edx);
49
50    /* cpuid 1.edx */
51    const unsigned int kvm_supported_word0_x86_features =
52        F(FPU) | 0 /*F(VME)*/ | 0 /*F(DE)*/ | 0/*F(PSE)*/ |
53        F(TSC) | 0/*F(MSR)*/ | 0 /*F(PAE)*/ | 0/*F(MCE)*/ |
54        0 /*F(CX8)*/ | F(APIC) | 0 /* Reserved */ | F(SEP) |
55        /*F(MTRR)*/ 0 | F(PGE) | 0/*F(MCA)*/ | F(CMOV) |
56        0 /*F(PAT)*/ | 0 /* F(PSE36)*/ | 0 /* PSN */ | 0/*F(CLFLSH)*/ |
57        0 /* Reserved, DS, ACPI */ | F(MMX) |
58        F(FXSR) | F(XMM) | F(XMM2) | 0/*F(SELFSNOOP)*/ |
59        0 /* HTT, TM, Reserved, PBE */;
60
61    /* cpuid 1.ecx */
62    const unsigned int kvm_supported_word4_x86_features =
63        F(XMM3) | 0 /*F(PCLMULQDQ)*/ | 0 /* DTES64, MONITOR */ |
64        0 /* DS-CPL, VMX, SMX, EST */ |
65        0 /* TM2 */ | F(SSSE3) | 0 /* CNXT-ID */ | 0 /* Reserved */ |
66        0 /*F(FMA)*/ | 0 /*F(CX16)*/ | 0 /* xTPR Update, PDCM */ |
67        0 /*F(PCID)*/ | 0 /* Reserved, DCA */ | F(XMM4_1) |
68        F(XMM4_2) | 0 /*F(X2APIC)*/ | 0 /*F(MOVBE)*/ | 0 /*F(POPCNT)*/ |
69        0 /* Reserved*/ | 0 /*F(AES)*/ | 0/*F(XSAVE)*/ | 0/*F(OSXSAVE)*/ | 0 /*F(AVX)*/ |
70        0 /*F(F16C)*/ | 0 /*F(RDRAND)*/;
71
72    /* cpuid 0x80000001.edx */
73    const unsigned int kvm_supported_word1_x86_features =
74        0 /*F(NX)*/ | 0/*F(RDTSCP)*/;  /*not support x86 64*/
75
76    /* cpuid 0x80000001.ecx */
77    const unsigned int kvm_supported_word6_x86_features = 0;
78
79#if 0
80    /* cpuid 0xC0000001.edx */
81    const unsigned int kvm_supported_word5_x86_features =
82        F(XSTORE) | F(XSTORE_EN) | F(XCRYPT) | F(XCRYPT_EN) |
83        F(ACE2) | F(ACE2_EN) | F(PHE) | F(PHE_EN) |
84        F(PMM) | F(PMM_EN);
85#endif
86
87    /* cpuid 7.0.ebx */
88    const unsigned int kvm_supported_word9_x86_features =
89        F(FSGSBASE) | F(BMI1) | F(HLE) | F(AVX2) | F(SMEP) |
90        F(BMI2) | F(ERMS) | 0 /*F(INVPCID)*/ | F(RTM);
91
92    /* Virtualize the return value according to the function. */
93
94    ZF_LOGD("cpuid function 0x%x index 0x%x eax 0x%x ebx 0%x ecx 0x%x edx 0x%x\n", function, index, eax, ebx, ecx, edx);
95
96    /* ref: http://www.sandpile.org/x86/cpuid.htm */
97
98    switch (function) {
99    case 0: /* Get highest function supported plus vendor ID */
100        if (eax > 0xb) {
101            eax = 0xb;
102        }
103        break;
104
105    case 1: /* Processor, info and feature. family, model, stepping */
106        edx &= kvm_supported_word0_x86_features;
107        ecx &= kvm_supported_word4_x86_features;
108        break;
109
110    case 2:
111    case 4: /* Cache and TLB descriptor information */
112        /* Simply pass through information from native CPUID. */
113        break;
114
115    case 7: /* Extended flags */
116        ebx &= kvm_supported_word9_x86_features;
117        break;
118
119    case 0xa: /* disable performance monitoring */
120        eax = ebx = ecx = edx = 0;
121        break;
122
123    case 0xb: /* Disable topology information */
124        eax = ebx = ecx = edx = 0;
125        break;
126
127    case VMM_CPUID_KVM_SIGNATURE: /* Unsupported KVM features. We are not KVM. */
128    case VMM_CPUID_KVM_FEATURES:
129        eax = ebx = ecx = edx = 0;
130        break;
131
132    case 0x80000000: /* Get highest extended function supported */
133        break;
134
135    case 0x80000001: /* extended processor info and feature bits */
136        ecx &= kvm_supported_word6_x86_features;
137        edx &= kvm_supported_word1_x86_features;
138        break;
139
140    case 0x80000002: /* Get processor name string. */
141    case 0x80000003:
142    case 0x80000004:
143    case 0x80000005:
144    case 0x80000006: /* Cache information. */
145        /* Pass through brand name from native CPUID. */
146        break;
147
148    case 0x80000008: /* Virtual and Physics address sizes */
149        break;
150
151    case 3: /* Processor serial number. */
152        break;
153    case 5: /* MONITOR / MWAIT */
154    case 6: /* Thermal management */
155    case 0x80000007: /* Advanced power management - unsupported. */
156    case 0xC0000002:
157    case 0xC0000003:
158    case 0xC0000004:
159        eax = ebx = ecx = edx = 0;
160        break;
161
162    default:
163        /* TODO: Adding more CPUID functions whenever necessary */
164        ZF_LOGE("CPUID unimplemented function 0x%x\n", function);
165        return -1;
166
167    }
168
169    val->eax = eax;
170    val->ebx = ebx;
171    val->ecx = ecx;
172    val->edx = edx;
173
174    ZF_LOGD("cpuid virt value eax 0x%x ebx 0x%x ecx 0x%x edx 0x%x\n", eax, ebx, ecx, edx);
175
176    return 0;
177
178}
179
180#if 0
181/* function 2 entries are STATEFUL. That is, repeated cpuid commands
182 * may return different values. This forces us to get_cpu() before
183 * issuing the first command, and also to emulate this annoying behavior
184 * in kvm_emulate_cpuid() using KVM_CPUID_FLAG_STATE_READ_NEXT */
185case 2:
186{
187    int t, times = entry->eax & 0xff;
188
189    entry->flags |= KVM_CPUID_FLAG_STATEFUL_FUNC;
190    entry->flags |= KVM_CPUID_FLAG_STATE_READ_NEXT;
191    for (t = 1; t < times; ++t) {
192        if (*nent >= maxnent) {
193            goto out;
194        }
195
196        do_cpuid_1_ent(&entry[t], function, 0);
197        entry[t].flags |= KVM_CPUID_FLAG_STATEFUL_FUNC;
198        ++*nent;
199    }
200    break;
201}
202/* function 4 has additional index. */
203case 4:
204{
205    int i, cache_type;
206
207    entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
208    /* read more entries until cache_type is zero */
209    for (i = 1; ; ++i) {
210        if (*nent >= maxnent) {
211            goto out;
212        }
213
214        cache_type = entry[i - 1].eax & 0x1f;
215        if (!cache_type) {
216            break;
217        }
218        do_cpuid_1_ent(&entry[i], function, i);
219        entry[i].flags |=
220            KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
221        ++*nent;
222    }
223    break;
224}
225case 7:
226{
227    entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
228    /* Mask ebx against host capability word 9 */
229    if (index == 0) {
230        entry->ebx &= kvm_supported_word9_x86_features;
231        cpuid_mask(&entry->ebx, 9);
232        // TSC_ADJUST is emulated
233        entry->ebx |= F(TSC_ADJUST);
234    } else {
235        entry->ebx = 0;
236    }
237    entry->eax = 0;
238    entry->ecx = 0;
239    entry->edx = 0;
240    break;
241}
242case 9:
243break;
244case 0xa:   /* Architectural Performance Monitoring */
245{
246    struct x86_pmu_capability cap;
247    union cpuid10_eax eax;
248    union cpuid10_edx edx;
249
250    perf_get_x86_pmu_capability(&cap);
251
252    /*
253     * Only support guest architectural pmu on a host
254     * with architectural pmu.
255     */
256    if (!cap.version) {
257        memset(&cap, 0, sizeof(cap));
258    }
259
260    eax.split.version_id = min(cap.version, 2);
261    eax.split.num_counters = cap.num_counters_gp;
262    eax.split.bit_width = cap.bit_width_gp;
263    eax.split.mask_length = cap.events_mask_len;
264
265    edx.split.num_counters_fixed = cap.num_counters_fixed;
266    edx.split.bit_width_fixed = cap.bit_width_fixed;
267    edx.split.reserved = 0;
268
269    entry->eax = eax.full;
270    entry->ebx = cap.events_mask;
271    entry->ecx = 0;
272    entry->edx = edx.full;
273    break;
274}
275/* function 0xb has additional index. */
276case 0xb:
277{
278    int i, level_type;
279
280    entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
281    /* read more entries until level_type is zero */
282    for (i = 1; ; ++i) {
283        if (*nent >= maxnent) {
284            goto out;
285        }
286
287        level_type = entry[i - 1].ecx & 0xff00;
288        if (!level_type) {
289            break;
290        }
291        do_cpuid_1_ent(&entry[i], function, i);
292        entry[i].flags |=
293            KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
294        ++*nent;
295    }
296    break;
297}
298case 0xd:
299{
300    int idx, i;
301
302    entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
303    for (idx = 1, i = 1; idx < 64; ++idx) {
304        if (*nent >= maxnent) {
305            goto out;
306        }
307
308        do_cpuid_1_ent(&entry[i], function, idx);
309        if (entry[i].eax == 0 || !supported_xcr0_bit(idx)) {
310            continue;
311        }
312        entry[i].flags |=
313            KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
314        ++*nent;
315        ++i;
316    }
317    break;
318}
319case KVM_CPUID_SIGNATURE:
320{
321    static const char signature[12] = "KVMKVMKVM\0\0";
322    const unsigned int *sigptr = (const unsigned int *)signature;
323    entry->eax = KVM_CPUID_FEATURES;
324    entry->ebx = sigptr[0];
325    entry->ecx = sigptr[1];
326    entry->edx = sigptr[2];
327    break;
328}
329case KVM_CPUID_FEATURES:
330entry->eax = (BIT(KVM_FEATURE_CLOCKSOURCE)) |
331             (BIT(KVM_FEATURE_NOP_IO_DELAY)) |
332             (BIT(KVM_FEATURE_CLOCKSOURCE2)) |
333             (BIT(KVM_FEATURE_ASYNC_PF)) |
334             (BIT(KVM_FEATURE_PV_EOI)) |
335             (BIT(KVM_FEATURE_CLOCKSOURCE_STABLE_BIT));
336
337if (sched_info_on())
338{
339    entry->eax |= (BIT(KVM_FEATURE_STEAL_TIME));
340}
341
342entry->ebx = 0;
343entry->ecx = 0;
344entry->edx = 0;
345break;
346case 0x80000019:
347entry->ecx = entry->edx = 0;
348break;
349case 0x8000001a:
350break;
351case 0x8000001d:
352break;
353/*Add support for Centaur's CPUID instruction*/
354case 0xC0000000:
355/*Just support up to 0xC0000004 now*/
356entry->eax = min(entry->eax, 0xC0000004);
357break;
358case 0xC0000001:
359entry->edx &= kvm_supported_word5_x86_features;
360cpuid_mask(&entry->edx, 5);
361break;
362case 3: /* Processor serial number */
363case 5: /* MONITOR/MWAIT */
364case 6: /* Thermal management */
365case 0x80000007: /* Advanced power management */
366case 0xC0000002:
367case 0xC0000003:
368case 0xC0000004:
369default:
370entry->eax = entry->ebx = entry->ecx = entry->edx = 0;
371break;
372}
373#endif
374
375/* VM exit handler: for the CPUID instruction. */
376int vm_cpuid_handler(vm_vcpu_t *vcpu)
377{
378
379    int ret;
380    struct cpuid_val val;
381
382    /* Read parameter information. */
383    unsigned int function, index;
384    if (vm_get_thread_context_reg(vcpu, VCPU_CONTEXT_EAX, &function)
385        || vm_get_thread_context_reg(vcpu, VCPU_CONTEXT_ECX, &index)) {
386        return VM_EXIT_HANDLE_ERROR;
387    }
388
389    /* Virtualise the CPUID instruction. */
390    ret = vm_cpuid_virt(function, index, &val, vcpu);
391    if (ret) {
392        return VM_EXIT_HANDLE_ERROR;
393    }
394
395    /* Set the return values in guest context. */
396    vm_set_thread_context_reg(vcpu, VCPU_CONTEXT_EAX, val.eax);
397    vm_set_thread_context_reg(vcpu, VCPU_CONTEXT_EBX, val.ebx);
398    vm_set_thread_context_reg(vcpu, VCPU_CONTEXT_ECX, val.ecx);
399    vm_set_thread_context_reg(vcpu, VCPU_CONTEXT_EDX, val.edx);
400
401    vm_guest_exit_next_instruction(vcpu->vcpu_arch.guest_state, vcpu->vcpu.cptr);
402
403    /* Return success. */
404    return VM_EXIT_HANDLED;
405}
406