summary refs log tree commit diff
path: root/hypervisor/src/kvm/x86_64.rs
blob: 06774f4dd2f6b65bf46e2a44060b0db9c2c3fb2c (plain) (blame)
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::convert::TryInto;

use kvm_sys::*;
use libc::E2BIG;
use sys_util::{ioctl_with_mut_ptr, Error, Result};

use super::{Kvm, KvmVcpu, KvmVm};
use crate::{
    CpuId, CpuIdEntry, HypervisorX86_64, IoapicRedirectionTableEntry, IoapicState, LapicState,
    PicState, PitChannelState, PitState, Regs, VcpuX86_64, VmX86_64,
};

type KvmCpuId = kvm::CpuId;

impl Kvm {
    pub fn get_cpuid(&self, kind: u64) -> Result<CpuId> {
        const KVM_MAX_ENTRIES: usize = 256;
        self.get_cpuid_with_initial_capacity(kind, KVM_MAX_ENTRIES)
    }

    fn get_cpuid_with_initial_capacity(&self, kind: u64, initial_capacity: usize) -> Result<CpuId> {
        let mut entries: usize = initial_capacity;

        loop {
            let mut kvm_cpuid = KvmCpuId::new(entries);

            let ret = unsafe {
                // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the
                // memory allocated for the struct. The limit is read from nent within KvmCpuId,
                // which is set to the allocated size above.
                ioctl_with_mut_ptr(self, kind, kvm_cpuid.as_mut_ptr())
            };
            if ret < 0 {
                let err = Error::last();
                match err.errno() {
                    E2BIG => {
                        // double the available memory for cpuid entries for kvm.
                        if let Some(val) = entries.checked_mul(2) {
                            entries = val;
                        } else {
                            return Err(err);
                        }
                    }
                    _ => return Err(err),
                }
            } else {
                return Ok(CpuId::from(&kvm_cpuid));
            }
        }
    }
}

impl<'a> From<&'a KvmCpuId> for CpuId {
    fn from(kvm_cpuid: &'a KvmCpuId) -> CpuId {
        let kvm_entries = kvm_cpuid.entries_slice();
        let mut cpu_id_entries = Vec::with_capacity(kvm_entries.len());

        for entry in kvm_entries {
            let cpu_id_entry = CpuIdEntry {
                function: entry.function,
                index: entry.index,
                eax: entry.eax,
                ebx: entry.ebx,
                ecx: entry.ecx,
                edx: entry.edx,
            };
            cpu_id_entries.push(cpu_id_entry)
        }
        CpuId { cpu_id_entries }
    }
}

impl HypervisorX86_64 for Kvm {
    fn get_supported_cpuid(&self) -> Result<CpuId> {
        self.get_cpuid(KVM_GET_SUPPORTED_CPUID())
    }

    fn get_emulated_cpuid(&self) -> Result<CpuId> {
        self.get_cpuid(KVM_GET_EMULATED_CPUID())
    }
}

impl VmX86_64 for KvmVm {
    type Vcpu = KvmVcpu;

    fn create_vcpu(&self, id: usize) -> Result<Self::Vcpu> {
        self.create_kvm_vcpu(id)
    }
}

impl VcpuX86_64 for KvmVcpu {
    fn get_regs(&self) -> Result<Regs> {
        Ok(Regs {})
    }
}

impl From<&kvm_pic_state> for PicState {
    fn from(item: &kvm_pic_state) -> Self {
        PicState {
            last_irr: item.last_irr,
            irr: item.irr,
            imr: item.imr,
            isr: item.isr,
            priority_add: item.priority_add,
            irq_base: item.irq_base,
            read_reg_select: item.read_reg_select != 0,
            poll: item.poll != 0,
            special_mask: item.special_mask != 0,
            init_state: item.init_state.into(),
            auto_eoi: item.auto_eoi != 0,
            rotate_on_auto_eoi: item.rotate_on_auto_eoi != 0,
            special_fully_nested_mode: item.special_fully_nested_mode != 0,
            use_4_byte_icw: item.init4 != 0,
            elcr: item.elcr,
            elcr_mask: item.elcr_mask,
        }
    }
}

impl From<&PicState> for kvm_pic_state {
    fn from(item: &PicState) -> Self {
        kvm_pic_state {
            last_irr: item.last_irr,
            irr: item.irr,
            imr: item.imr,
            isr: item.isr,
            priority_add: item.priority_add,
            irq_base: item.irq_base,
            read_reg_select: item.read_reg_select as u8,
            poll: item.poll as u8,
            special_mask: item.special_mask as u8,
            init_state: item.init_state as u8,
            auto_eoi: item.auto_eoi as u8,
            rotate_on_auto_eoi: item.rotate_on_auto_eoi as u8,
            special_fully_nested_mode: item.special_fully_nested_mode as u8,
            init4: item.use_4_byte_icw as u8,
            elcr: item.elcr,
            elcr_mask: item.elcr_mask,
        }
    }
}

impl From<&kvm_ioapic_state> for IoapicState {
    fn from(item: &kvm_ioapic_state) -> Self {
        let mut state = IoapicState {
            base_address: item.base_address,
            ioregsel: item.ioregsel,
            ioapicid: item.id,
            current_interrupt_level_bitmap: item.irr,
            redirect_table: [IoapicRedirectionTableEntry::default(); 24],
        };
        for (in_state, out_state) in item.redirtbl.iter().zip(state.redirect_table.iter_mut()) {
            *out_state = in_state.into();
        }
        state
    }
}

impl From<&IoapicRedirectionTableEntry> for kvm_ioapic_state__bindgen_ty_1 {
    fn from(item: &IoapicRedirectionTableEntry) -> Self {
        kvm_ioapic_state__bindgen_ty_1 {
            // IoapicRedirectionTableEntry layout matches the exact bit layout of a hardware
            // ioapic redirection table entry, so we can simply do a 64-bit copy
            bits: item.get(0, 64),
        }
    }
}

impl From<&kvm_ioapic_state__bindgen_ty_1> for IoapicRedirectionTableEntry {
    fn from(item: &kvm_ioapic_state__bindgen_ty_1) -> Self {
        let mut entry = IoapicRedirectionTableEntry::default();
        // Safe because the 64-bit layout of the IoapicRedirectionTableEntry matches the kvm_sys
        // table entry layout
        entry.set(0, 64, unsafe { item.bits as u64 });
        entry
    }
}

impl From<&IoapicState> for kvm_ioapic_state {
    fn from(item: &IoapicState) -> Self {
        let mut state = kvm_ioapic_state {
            base_address: item.base_address,
            ioregsel: item.ioregsel,
            id: item.ioapicid,
            irr: item.current_interrupt_level_bitmap,
            ..Default::default()
        };
        for (in_state, out_state) in item.redirect_table.iter().zip(state.redirtbl.iter_mut()) {
            *out_state = in_state.into();
        }
        state
    }
}

impl From<&LapicState> for kvm_lapic_state {
    fn from(item: &LapicState) -> Self {
        let mut state = kvm_lapic_state::default();
        // There are 64 lapic registers
        for (reg, value) in item.regs.iter().enumerate() {
            // Each lapic register is 16 bytes, but only the first 4 are used
            let reg_offset = 16 * reg;
            let sliceu8 = unsafe {
                // This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
                // to_le_bytes() produces an array of u8, not i8(c_char).
                std::mem::transmute::<&mut [i8], &mut [u8]>(
                    &mut state.regs[reg_offset..reg_offset + 4],
                )
            };
            sliceu8.copy_from_slice(&value.to_le_bytes());
        }
        state
    }
}

impl From<&kvm_lapic_state> for LapicState {
    fn from(item: &kvm_lapic_state) -> Self {
        let mut state = LapicState { regs: [0; 64] };
        // There are 64 lapic registers
        for reg in 0..64 {
            // Each lapic register is 16 bytes, but only the first 4 are used
            let reg_offset = 16 * reg;
            let bytes = unsafe {
                // This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
                // from_le_bytes() only works on arrays of u8, not i8(c_char).
                std::mem::transmute::<&[i8], &[u8]>(&item.regs[reg_offset..reg_offset + 4])
            };
            state.regs[reg] = u32::from_le_bytes(bytes.try_into().unwrap());
        }
        state
    }
}

impl From<&PitState> for kvm_pit_state2 {
    fn from(item: &PitState) -> Self {
        kvm_pit_state2 {
            channels: [
                kvm_pit_channel_state::from(&item.channels[0]),
                kvm_pit_channel_state::from(&item.channels[1]),
                kvm_pit_channel_state::from(&item.channels[2]),
            ],
            flags: item.flags,
            ..Default::default()
        }
    }
}

impl From<&kvm_pit_state2> for PitState {
    fn from(item: &kvm_pit_state2) -> Self {
        PitState {
            channels: [
                PitChannelState::from(&item.channels[0]),
                PitChannelState::from(&item.channels[1]),
                PitChannelState::from(&item.channels[2]),
            ],
            flags: item.flags,
        }
    }
}

impl From<&PitChannelState> for kvm_pit_channel_state {
    fn from(item: &PitChannelState) -> Self {
        kvm_pit_channel_state {
            count: item.count,
            latched_count: item.latched_count,
            count_latched: item.count_latched as u8,
            status_latched: item.status_latched as u8,
            status: item.status,
            read_state: item.read_state as u8,
            write_state: item.write_state as u8,
            // kvm's write_latch only stores the low byte of the reload value
            write_latch: item.reload_value as u8,
            rw_mode: item.rw_mode as u8,
            mode: item.mode,
            bcd: item.bcd as u8,
            gate: item.gate as u8,
            count_load_time: item.count_load_time as i64,
        }
    }
}

impl From<&kvm_pit_channel_state> for PitChannelState {
    fn from(item: &kvm_pit_channel_state) -> Self {
        PitChannelState {
            count: item.count,
            latched_count: item.latched_count,
            count_latched: item.count_latched.into(),
            status_latched: item.status_latched != 0,
            status: item.status,
            read_state: item.read_state.into(),
            write_state: item.write_state.into(),
            // kvm's write_latch only stores the low byte of the reload value
            reload_value: item.write_latch as u16,
            rw_mode: item.rw_mode.into(),
            mode: item.mode,
            bcd: item.bcd != 0,
            gate: item.gate != 0,
            count_load_time: item.count_load_time as u64,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        DeliveryMode, DeliveryStatus, DestinationMode, IoapicRedirectionTableEntry, IoapicState,
        LapicState, PicInitState, PicState, PitChannelState, PitRWMode, PitRWState, PitState,
        TriggerMode,
    };
    use kvm_sys::*;

    use super::Kvm;
    use crate::HypervisorX86_64;

    #[test]
    fn get_supported_cpuid() {
        let hypervisor = Kvm::new().unwrap();
        let cpuid = hypervisor.get_supported_cpuid().unwrap();
        assert!(cpuid.cpu_id_entries.len() > 0);
    }

    #[test]
    fn get_emulated_cpuid() {
        let hypervisor = Kvm::new().unwrap();
        let cpuid = hypervisor.get_emulated_cpuid().unwrap();
        assert!(cpuid.cpu_id_entries.len() > 0);
    }

    #[test]
    fn entries_double_on_error() {
        let hypervisor = Kvm::new().unwrap();
        let cpuid = hypervisor
            .get_cpuid_with_initial_capacity(KVM_GET_SUPPORTED_CPUID(), 4)
            .unwrap();
        assert!(cpuid.cpu_id_entries.len() > 4);
    }

    #[test]
    fn pic_state() {
        let state = PicState {
            last_irr: 0b00000001,
            irr: 0b00000010,
            imr: 0b00000100,
            isr: 0b00001000,
            priority_add: 0b00010000,
            irq_base: 0b00100000,
            read_reg_select: false,
            poll: true,
            special_mask: true,
            init_state: PicInitState::Icw3,
            auto_eoi: true,
            rotate_on_auto_eoi: false,
            special_fully_nested_mode: true,
            use_4_byte_icw: true,
            elcr: 0b01000000,
            elcr_mask: 0b10000000,
        };

        let kvm_state = kvm_pic_state::from(&state);

        assert_eq!(kvm_state.last_irr, 0b00000001);
        assert_eq!(kvm_state.irr, 0b00000010);
        assert_eq!(kvm_state.imr, 0b00000100);
        assert_eq!(kvm_state.isr, 0b00001000);
        assert_eq!(kvm_state.priority_add, 0b00010000);
        assert_eq!(kvm_state.irq_base, 0b00100000);
        assert_eq!(kvm_state.read_reg_select, 0);
        assert_eq!(kvm_state.poll, 1);
        assert_eq!(kvm_state.special_mask, 1);
        assert_eq!(kvm_state.init_state, 0b10);
        assert_eq!(kvm_state.auto_eoi, 1);
        assert_eq!(kvm_state.rotate_on_auto_eoi, 0);
        assert_eq!(kvm_state.special_fully_nested_mode, 1);
        assert_eq!(kvm_state.auto_eoi, 1);
        assert_eq!(kvm_state.elcr, 0b01000000);
        assert_eq!(kvm_state.elcr_mask, 0b10000000);

        let orig_state = PicState::from(&kvm_state);
        assert_eq!(state, orig_state);
    }

    #[test]
    fn ioapic_state() {
        let mut entry = IoapicRedirectionTableEntry::default();
        // default entry should be 0
        assert_eq!(entry.get(0, 64), 0);

        // set some values on our entry
        entry.set_vector(0b11111111);
        entry.set_delivery_mode(DeliveryMode::SMI);
        entry.set_dest_mode(DestinationMode::Physical);
        entry.set_delivery_status(DeliveryStatus::Pending);
        entry.set_polarity(1);
        entry.set_remote_irr(true);
        entry.set_trigger_mode(TriggerMode::Level);
        entry.set_interrupt_mask(true);
        entry.set_dest_id(0b10101010);

        // Bit repr as:  destid-reserved--------------------------------flags----vector--
        let bit_repr = 0b1010101000000000000000000000000000000000000000011111001011111111;
        // where flags is [interrupt_mask(1), trigger_mode(Level=1), remote_irr(1), polarity(1),
        //   delivery_status(Pending=1), dest_mode(Physical=0), delivery_mode(SMI=010)]

        assert_eq!(entry.get(0, 64), bit_repr);

        let state = IoapicState {
            base_address: 1,
            ioregsel: 2,
            ioapicid: 4,
            current_interrupt_level_bitmap: 8,
            redirect_table: [entry; 24],
        };

        let kvm_state = kvm_ioapic_state::from(&state);
        assert_eq!(kvm_state.base_address, 1);
        assert_eq!(kvm_state.ioregsel, 2);
        assert_eq!(kvm_state.id, 4);
        assert_eq!(kvm_state.irr, 8);
        assert_eq!(kvm_state.pad, 0);
        // check our entries
        for i in 0..24 {
            assert_eq!(unsafe { kvm_state.redirtbl[i].bits }, bit_repr);
        }

        // compare with a conversion back
        assert_eq!(state, IoapicState::from(&kvm_state));
    }

    #[test]
    fn lapic_state() {
        let mut state = LapicState { regs: [0; 64] };
        // Apic id register, 4 bytes each with a different bit set
        state.regs[2] = 1 | 2 << 8 | 4 << 16 | 8 << 24;

        let kvm_state = kvm_lapic_state::from(&state);

        // check little endian bytes in kvm_state
        for i in 0..4 {
            assert_eq!(
                unsafe { std::mem::transmute::<i8, u8>(kvm_state.regs[32 + i]) } as u8,
                2u8.pow(i as u32)
            );
        }

        // Test converting back to a LapicState
        assert_eq!(state, LapicState::from(&kvm_state));
    }

    #[test]
    fn pit_state() {
        let channel = PitChannelState {
            count: 256,
            latched_count: 512,
            count_latched: PitRWState::LSB,
            status_latched: false,
            status: 7,
            read_state: PitRWState::MSB,
            write_state: PitRWState::Word1,
            reload_value: 8,
            rw_mode: PitRWMode::Both,
            mode: 5,
            bcd: false,
            gate: true,
            count_load_time: 1024,
        };

        let kvm_channel = kvm_pit_channel_state::from(&channel);

        // compare the various field translations
        assert_eq!(kvm_channel.count, 256);
        assert_eq!(kvm_channel.latched_count, 512);
        assert_eq!(kvm_channel.count_latched, 1);
        assert_eq!(kvm_channel.status_latched, 0);
        assert_eq!(kvm_channel.status, 7);
        assert_eq!(kvm_channel.read_state, 2);
        assert_eq!(kvm_channel.write_state, 4);
        assert_eq!(kvm_channel.write_latch, 8);
        assert_eq!(kvm_channel.rw_mode, 3);
        assert_eq!(kvm_channel.mode, 5);
        assert_eq!(kvm_channel.bcd, 0);
        assert_eq!(kvm_channel.gate, 1);
        assert_eq!(kvm_channel.count_load_time, 1024);

        // convert back and compare
        assert_eq!(channel, PitChannelState::from(&kvm_channel));

        // convert the full pitstate
        let state = PitState {
            channels: [channel, channel, channel],
            flags: 255,
        };
        let kvm_state = kvm_pit_state2::from(&state);

        assert_eq!(kvm_state.flags, 255);

        // compare a channel
        assert_eq!(channel, PitChannelState::from(&kvm_state.channels[0]));
        // convert back and compare
        assert_eq!(state, PitState::from(&kvm_state));
    }
}