Compare commits

..

638 Commits

Author SHA1 Message Date
Rob Bradford
bb02c28b62 build: Update for v0.14.1 bug fix release
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-31 16:56:05 +01:00
Sebastien Boeuf
b2c96dea24 vmm: Add missing syscall for vCPU unplug
clock_nanosleep() is triggered when hot-unplugging a vCPU.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
(cherry picked from commit 46f96f27a4)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-31 16:56:05 +01:00
Rob Bradford
2c81312433 vm-virtio: queue: Fix descriptor chain validation
DescriptorChain::is_valid() wrongly used .checked_offset() to attempt to
validate that the descriptor's data is in valid memory. This works in
all cases except where the guest has placed the data at the very end of
the guest memory as the offset + offset will be outside the range (as
the combined offset will be the next byte and as such out of the guest
memory). Instead use the function .check_range() takes an offset and a
length to validate

This fixes issues see with error messages featuring the
DescriptorChainTooShort error.

Fixes: #2424

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
(cherry picked from commit 1eb4ebe3d1)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-31 16:56:05 +01:00
Anatol Belski
e8ee29b4fe CpuManager: Fix MMIO read handling
There are two parts:

- Unconditionally zero the output area. The length of the incoming
  vector has been seen from 1 to 4 bytes, even though just the first
  byte might need to be handled. But also, this ensures any possibly
  unhandled offset will return zeroed result to the caller. The former
  implementation used an I/O port which seems to behave differently from
  MMIO and wouldn't require explicit output zeroing.
- An access with zero offset still takes place and needs to be handled.

Fixes #2437.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
(cherry picked from commit 9e9aba7c0b)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-31 16:56:05 +01:00
Anatol Belski
498939c297 hyperv: Fix CPU hotadd
The following is from the Hyper-V specification v6.0b.

Cpuid leaf 0x40000003 EDX:

Bit 3: Support for physical CPU dynamic partitioning events is
available.

When Windows determines to be running under a hypervisor, it will
require this cpuid bit to be set to support dynamic CPU operations.

Cpuid leaf 0x40000004 EAX:

Bit 5: Recommend using relaxed timing for this partition. If
used, the VM should disable any watchdog timeouts that
rely on the timely delivery of external interrupts.

This bit has been figured out as required after seeing guest BSOD
when CPU hotplug bit is enabled. Race conditions seem to arise after a
hotplug operation, when a system watchdog has expired.

Closes #1799.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
(cherry picked from commit 5b168f54a6)
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-31 16:56:05 +01:00
Rob Bradford
84b3992146 build: Update Cargo.lock for release
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:46:55 +00:00
Rob Bradford
40c63dcf5e build: Update for 0.14.0 release
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:27 +00:00
Rob Bradford
970bc05271 aarch64: Address Rust 1.51.0 clippy issue (vec_init_then_push)
--> arch/src/aarch64/mod.rs:82:5
    |
82  | /     let mut regions = Vec::new();
83  | |     // 0 ~ 256 MiB: Reserved
84  | |     regions.push((
85  | |         GuestAddress(0),
...   |
107 | |         RegionType::Ram,
108 | |     ));
    | |_______^ help: consider using the `vec![]` macro: `let mut regions = vec![..];`
    |
    = note: `-D clippy::vec-init-then-push` implied by `-D warnings`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
4da6bcd5d5 aarch64: Address Rust 1.51.0 clippy issue (from_over_into)
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
Error:   --> devices/src/legacy/rtc_pl031.rs:73:1
   |
73 | impl Into<libc::clockid_t> for ClockType {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `-D clippy::from-over-into` implied by `-D warnings`
   = help: consider to implement `From` instead
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
3671f5e94c aarch64: Address Rust 1.51.0 clippy issue (redundant_slicing)
error: redundant slicing of the whole range
Error:    --> devices/src/legacy/gpio_pl061.rs:298:37
    |
298 |             let value = read_le_u32(&data[..]);
    |                                     ^^^^^^^^^ help: use the original slice instead: `data`
    |
    = note: `-D clippy::redundant-slicing` implied by `-D warnings`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
40da6210f4 aarch64: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
error: name `GPIOInterruptDisabled` contains a capitalized acronym

Error:   --> devices/src/legacy/gpio_pl061.rs:46:5
   |
46 |     GPIOInterruptDisabled,
   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GpioInterruptDisabled`
   |
   = note: `-D clippy::upper-case-acronyms` implied by `-D warnings`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
3c6dfd7709 tdx: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
error: name `FinalizeTDX` contains a capitalized acronym
   --> vmm/src/vm.rs:274:5
    |
274 |     FinalizeTDX(hypervisor::HypervisorVmError),
    |     ^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `FinalizeTdx`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
e294688904 hypervisor: Address Rust 1.51.0 clippy issue (from_over_into)
warning: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
  --> hypervisor/src/vm.rs:41:1
   |
41 | impl Into<u64> for DataMatch {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(clippy::from_over_into)]` on by default
   = help: consider to implement `From` instead
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into

warning: 1 warning emitted

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
5c010d489d tests: Address Rust 1.51.0 clippy issue (needless_question_mark)
error: Question mark operator is useless here
   --> tests/integration.rs:494:13
    |
494 | /             Ok(self
495 | |                 .ssh_command("grep MemTotal /proc/meminfo | grep -o \"[0-9]*\"")?
496 | |                 .trim()
497 | |                 .parse()
498 | |                 .map_err(Error::Parsing)?)
    | |__________________________________________^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
c5d15fd938 devices: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
warning: name `AcpiPMTimerDevice` contains a capitalized acronym
   --> devices/src/acpi.rs:175:12
    |
175 | pub struct AcpiPMTimerDevice {
    |            ^^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `AcpiPmTimerDevice`
    |
    = note: `#[warn(clippy::upper_case_acronyms)]` on by default
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
827229d8e4 pci: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
warning: name `IORegion` contains a capitalized acronym
   --> pci/src/configuration.rs:320:5
    |
320 |     IORegion = 0x01,
    |     ^^^^^^^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `IoRegion`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
6837de9057 vmm: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
warning: name `ConvertFromUTF8` contains a capitalized acronym
  --> virtio-devices/src/vsock/unix/mod.rs:32:5
   |
32 |     ConvertFromUTF8(std::str::Utf8Error),
   |     ^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `ConvertFromUtf8`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
7c9a83f6e1 virtio-devices: Address Rust 1.51.0 clippy issue (needless_question_mark)
warning: Question mark operator is useless here
   --> virtio-devices/src/seccomp_filters.rs:485:5
    |
485 | /     Ok(SeccompFilter::new(
486 | |         rules.into_iter().collect(),
487 | |         SeccompAction::Log,
488 | |     )?)
    | |_______^
    |
    = note: `#[warn(clippy::needless_question_mark)]` on by default
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
help: try
    |
485 |     SeccompFilter::new(
486 |         rules.into_iter().collect(),
487 |         SeccompAction::Log,
488 |     )
    |

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
2571cc8041 virtio-devices: Address Rust 1.51.0 clippy issue (vec_init_then_push)
warning: calls to `push` immediately after creation
   --> virtio-devices/src/vhost_user/net.rs:291:13
    |
291 | /             let mut interrupt_list_sub: Vec<(Option<EventFd>, Queue)> = Vec::with_capacity(2);
292 | |             interrupt_list_sub.push(vu_interrupt_list.remove(0));
293 | |             interrupt_list_sub.push(vu_interrupt_list.remove(0));
    | |_________________________________________________________________^ help: consider using the `vec![]` macro: `let mut interrupt_list_sub: Vec<(Option<EventFd>, Queue)> = vec![..];`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
3b8d1f1411 vmm: Address Rust 1.51.0 clippy issue (vec_init_then_push)
warning: calls to `push` immediately after creation
   --> vmm/src/cpu.rs:630:9
    |
630 | /         let mut cpuid_patches = Vec::new();
631 | |
632 | |         // Patch tsc deadline timer bit
633 | |         cpuid_patches.push(CpuidPatch {
...   |
662 | |             edx_bit: Some(MTRR_EDX_BIT),
663 | |         });
    | |___________^ help: consider using the `vec![]` macro: `let mut cpuid_patches = vec![..];`
    |
    = note: `#[warn(clippy::vec_init_then_push)]` on by default
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
9762c8bc28 vmm: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
warning: name `LocalAPIC` contains a capitalized acronym
   --> vmm/src/cpu.rs:197:8
    |
197 | struct LocalAPIC {
    |        ^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `LocalApic`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
7c302373ed vmm: Address Rust 1.51.0 clippy issue (needless_question_mark)
warning: Question mark operator is useless here
   --> vmm/src/seccomp_filters.rs:493:5
    |
493 | /     Ok(SeccompFilter::new(
494 | |         rules.into_iter().collect(),
495 | |         SeccompAction::Trap,
496 | |     )?)
    | |_______^
    |
    = note: `#[warn(clippy::needless_question_mark)]` on by default
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
help: try
    |
493 |     SeccompFilter::new(
494 |         rules.into_iter().collect(),
495 |         SeccompAction::Trap,
496 |     )
    |

warning: Question mark operator is useless here
   --> vmm/src/seccomp_filters.rs:507:5
    |
507 | /     Ok(SeccompFilter::new(
508 | |         rules.into_iter().collect(),
509 | |         SeccompAction::Log,
510 | |     )?)
    | |_______^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
help: try
    |
507 |     SeccompFilter::new(
508 |         rules.into_iter().collect(),
509 |         SeccompAction::Log,
510 |     )
    |

warning: Question mark operator is useless here
   --> vmm/src/vm.rs:887:9
    |
887 |         Ok(CString::new(cmdline).map_err(Error::CmdLineCString)?)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `CString::new(cmdline).map_err(Error::CmdLineCString)`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
19c5e91b6e main: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
warning: name `StartVMMThread` contains a capitalized acronym
  --> src/main.rs:50:5
   |
50 |     StartVMMThread(#[source] vmm::Error),
   |     ^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `StartVmmThread`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
4a5939973c ch-remote: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
warning: name `InvalidCPUCount` contains a capitalized acronym
  --> src/bin/ch-remote.rs:24:5
   |
24 |     InvalidCPUCount(std::num::ParseIntError),
   |     ^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `InvalidCpuCount`
   |
   = note: `#[warn(clippy::upper_case_acronyms)]` on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
0c27f69f1c hypervisor: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
warning: name `TranslateGVA` contains a capitalized acronym
  --> hypervisor/src/arch/emulator/mod.rs:51:5
   |
51 |     TranslateGVA(#[source] anyhow::Error),
   |     ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `TranslateGva`
   |
   = note: `#[warn(clippy::upper_case_acronyms)]` on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
7d5d9bdcf4 vhost_user_block: Address Rust 1.51.0 clippy issue (redundant_slicing)
error: redundant slicing of the whole range
   --> vhost_user_block/src/lib.rs:396:31
    |
396 |         right.copy_from_slice(&data[..]);
    |                               ^^^^^^^^^ help: use the original slice instead: `data`
    |
    = note: `-D clippy::redundant-slicing` implied by `-D warnings`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
85ad72490f arch: Address Rust 1.51.0 clippy issue (upper_case_acronyms)
error: name `RSDPPastRamEnd` contains a capitalized acronym
  --> arch/src/lib.rs:59:5
   |
59 |     RSDPPastRamEnd,
   |     ^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `RsdpPastRamEnd`
   |
   = note: `-D clippy::upper-case-acronyms` implied by `-D warnings`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
80e48b545d block_util: Address Rust 1.51.0 clippy issue (ptr-arg)
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
  --> block_util/src/lib.rs:68:31
   |
68 | fn build_device_id(disk_path: &PathBuf) -> result::Result<String, Error> {
   |                               ^^^^^^^^ help: change this to: `&Path`
   |
   = note: `-D clippy::ptr-arg` implied by `-D warnings`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg

error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
  --> block_util/src/lib.rs:83:39
   |
83 | pub fn build_disk_image_id(disk_path: &PathBuf) -> Vec<u8> {
   |                                       ^^^^^^^^ help: change this to: `&Path`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg

error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
  --> block_util/src/lib.rs:68:31
   |
68 | fn build_device_id(disk_path: &PathBuf) -> result::Result<String, Error> {
   |                               ^^^^^^^^ help: change this to: `&Path`
   |
   = note: `-D clippy::ptr-arg` implied by `-D warnings`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg

error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
  --> block_util/src/lib.rs:83:39
   |
83 | pub fn build_disk_image_id(disk_path: &PathBuf) -> Vec<u8> {
   |                                       ^^^^^^^^ help: change this to: `&Path`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
eb18ea61f4 net_util: Address Rust 1.51.0 clippy issue (redundant_slicing)
error: redundant slicing of the whole range
  --> net_util/src/mac.rs:60:35
   |
60 |         bytes[..].copy_from_slice(&src[..]);
   |                                   ^^^^^^^^ help: use the original slice instead: `src`
   |
   = note: `-D clippy::redundant-slicing` implied by `-D warnings`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
bfbd75b2cf qcow: Address Rust 1.51.0 clippy issue (redundant-semicolons)
error: unnecessary trailing semicolon
    --> qcow/src/qcow.rs:2050:14
     |
2050 |             };
     |              ^ help: remove this semicolon
     |
     = note: `-D redundant-semicolons` implied by `-D warnings`

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
6dc3d60b2d block_util: Address Rust 1.51.0 clippy issue (upper_case_acronyms)
error: name `TYPE_UNKNOWN` contains a capitalized acronym
  --> vm-virtio/src/lib.rs:48:5
   |
48 |     TYPE_UNKNOWN = 0xFF,
   |     ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `Type_Unknown`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

error: name `GetDeviceID` contains a capitalized acronym
   --> block_util/src/lib.rs:138:5
    |
138 |     GetDeviceID,
    |     ^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GetDeviceId`
    |
    = note: `-D clippy::upper-case-acronyms` implied by `-D warnings`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
aa34d545f6 vm-virtio, virtio-devices: Address Rust 1.51.0 clippy issue (upper_case_acronyms)
error: name `TYPE_UNKNOWN` contains a capitalized acronym
  --> vm-virtio/src/lib.rs:48:5
   |
48 |     TYPE_UNKNOWN = 0xFF,
   |     ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `Type_Unknown`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
261c039831 acpi_tables: Address Rust 1.51.0 clippy issue (vec_init_then_push)
error: calls to `push` immediately after creation
    --> acpi_tables/src/aml.rs:1194:9
     |
1194 | /         let mut bytes = Vec::new();
1195 | |         bytes.push(0x8a); /* CreateDWordFieldOp */
     | |_________________________^ help: consider using the `vec![]` macro: `let mut bytes = vec![..];`
     |
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
db6516931d acpi_tables: Address Rust 1.51.0 clippy issue (upper_case_acronyms)
error: name `SDT` contains a capitalized acronym
  --> acpi_tables/src/sdt.rs:27:12
   |
27 | pub struct SDT {
   |            ^^^ help: consider making the acronym lowercase, except the initial letter: `Sdt`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
030d6046b2 api_client: Address Rust 1.51.0 clippy issue (upper_case_acroynms)
--> api_client/src/lib.rs:40:5
   |
40 |     OK,
   |     ^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ok`
   |
   = note: `-D clippy::upper-case-acronyms` implied by `-D warnings`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-26 11:32:09 +00:00
Rob Bradford
7390475636 arch: Report use of deprecated LinuxBoot protocol
With CONFIG_PVH in stable kernels for some time we should deprecate the
use of alternative boot methods since this will lead to a much simpler
boot flow and CI process.

See: #2231

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-25 13:59:19 +00:00
Rob Bradford
c4564f3ba8 block_util: Enhance error reporting for virtio-block usage
There are multiple reports of DescriptorChainTooShort errors and so add
some extra debugging to aid the debugging of this issue.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-25 13:41:54 +01:00
Wei Liu
27ba8133a4 vmm: interrupt: drop RoutingEntryExt trait
We can now directly use associated functions.

This simplifies code and causes no functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-03-25 10:13:43 +01:00
Wei Liu
f5c550affb vmm: interrupt: simplify interrupt handling traits
Drop the generic type E and use IrqRoutngEntry directly. This allows
dropping a bunch of trait bounds from code.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-03-25 10:13:43 +01:00
Wei Liu
1d9f27c9fb vmm: interrupt: extract common code from MSHV and KVM
Their make_entry functions look the same now. Extract the logic to a
common function.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-03-25 10:13:43 +01:00
Rob Bradford
8b7aafad16 virtio-devices: block: Remove unused members of Error enum
These are residual enum members from a previous refactoring.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-24 17:27:41 +01:00
Rob Bradford
46d082763f vm-virtio: queue: Fix comment for DescriptorChain::has_next()
This returns trues if this descriptor has another descriptor linked to
it. Not whether this descriptor chain has another one following it.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-24 17:26:53 +01:00
Rob Bradford
6170ba06d1 README.md: Update URL for hypervisor-fw
The 0.3.0 release (re)supports booting stock Ubuntu images so users
should be pointed towards that.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-24 11:41:13 +01:00
Sebastien Boeuf
63304d0be7 net_util: Check descriptor size
There is no point in queueing an empty descriptor in the list of iovecs.
Let's simply ignore such case and avoid some unnecessary processing.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-24 11:41:05 +01:00
Sebastien Boeuf
1cd186c83c docs: Update hotplug documentation with ch-remote
Replacing old instructions using curl with ch-remote ones.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-23 14:55:49 -07:00
Sebastien Boeuf
5581486149 docs: Expand memory hotplug documentation for virtio-mem
The hotplug documentation was missing the explanation on how to use
memory hotplug with virtio-mem.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-23 14:55:49 -07:00
Sebastien Boeuf
3e3c163285 docs: Extend documentation with PCI device hotplug
Add explanations about PCI device hotplug along with CPU and memory
hotplug.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-23 14:55:49 -07:00
Sebastien Boeuf
6190e64f2f docs: Add seccomp documentation
Document that seccomp is on and how to disable it along with a pointer
on how to identify missing syscalls during development.

Fixes #993

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-23 18:41:30 +01:00
Rob Bradford
88c30764e0 tests: Move test_infra from a module to crate
It must be specified as excluded from the workspace as it must not be
built on non-test targets due to issues with the ssh2 dependency and the
musl toolchain.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-23 18:04:50 +01:00
dependabot-preview[bot]
91145afa9b build(deps): bump serde from 1.0.124 to 1.0.125 in /fuzz
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.124 to 1.0.125.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.124...v1.0.125)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-23 11:52:04 +00:00
dependabot-preview[bot]
50d57b2b6f build(deps): bump serde_derive from 1.0.124 to 1.0.125 in /fuzz
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.124 to 1.0.125.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.124...v1.0.125)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-23 11:15:32 +00:00
Vineeth Pillai
68401e6e4a hypervisor:mshv: Support the move of MSI routing to kernel
Signed-off-by: Vineeth Pillai <viremana@linux.microsoft.com>
2021-03-23 11:06:13 +01:00
Vineeth Pillai
7fad74cb04 hypervisor: refactor vec_with_array_field function
refactor vec_with_array_field to common hypervisor code
so that mshv can also make use of it.

Signed-off-by: Vineeth Pillai <viremana@linux.microsoft.com>
2021-03-23 11:06:13 +01:00
dependabot-preview[bot]
b8c0cd5cd4 build(deps): bump serde_derive from 1.0.124 to 1.0.125
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.124 to 1.0.125.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.124...v1.0.125)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-23 08:10:45 +00:00
dependabot-preview[bot]
874ffe60ca build(deps): bump serde from 1.0.124 to 1.0.125
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.124 to 1.0.125.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.124...v1.0.125)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-23 07:28:35 +00:00
dependabot-preview[bot]
e9793020c2 build(deps): bump libc from 0.2.90 to 0.2.91
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.90 to 0.2.91.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.90...0.2.91)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-23 06:56:04 +00:00
Sebastien Boeuf
d370ea585b deps: bump iced-x86 from 1.10.3 to 1.11.0
Bumps iced-x86 from 1.10.3 to 1.11.0.

Manual update of the code was needed since memory_displacement() was
deprecated and replaced with either memory_displacement32() or
memory_displacement64().

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-22 15:17:09 +00:00
Sebastien Boeuf
1e11e6789a vmm: device_manager: Factorize passthrough_device creation
There's no need to have the code creating the passthrough_device being
duplicated since we can factorize it in a function used in both cases
(both cold plugged and hot plugged devices VFIO devices).

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-22 10:47:33 +01:00
dependabot-preview[bot]
f6da2bb5e1 build(deps): bump anyhow from 1.0.38 to 1.0.39 in /fuzz
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.38 to 1.0.39.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.38...1.0.39)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-22 07:47:26 +00:00
dependabot-preview[bot]
987e52090f build(deps): bump anyhow from 1.0.38 to 1.0.39
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.38 to 1.0.39.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.38...1.0.39)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-20 08:05:06 +00:00
dependabot-preview[bot]
195ac4c5dc build(deps): bump vhost from ee3e872 to 05cd8b2
Bumps [vhost](https://github.com/rust-vmm/vhost) from `ee3e872` to `05cd8b2`.
- [Release notes](https://github.com/rust-vmm/vhost/releases)
- [Commits](ee3e872270...05cd8b2ad3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-19 07:52:46 +00:00
dependabot-preview[bot]
685a1760f3 build(deps): bump libc from 0.2.89 to 0.2.90 in /fuzz
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.89 to 0.2.90.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.89...0.2.90)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-19 07:52:32 +00:00
dependabot-preview[bot]
e39924d45a build(deps): bump libc from 0.2.89 to 0.2.90
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.89 to 0.2.90.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.89...0.2.90)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-18 21:29:25 +00:00
Sebastien Boeuf
b06fd80fa9 vmm: device_manager: Use DeviceTree to store PCI devices
Extend and use the existing DeviceTree to retrieve useful information
related to PCI devices. This removes the duplication with pci_devices
field which was internal to the DeviceManager.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-18 15:26:25 +01:00
Sebastien Boeuf
c8c3cad8cb vmm: device_manager: Update structure holding PCI IRQs
Make the code a bit clearer by changing the naming of the structure
holding the list of IRQs reserved for PCI devices. It is also modified
into an array of 32 entries since we know this is the amount of PCI
slots that is supported.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-18 15:26:25 +01:00
Sebastien Boeuf
e311fd66cd vmm: device_manager: Remove the need for Any
We define a new enum in order to classify PCI device under virtio or
VFIO. This is a cleaner approach than using the Any trait, and
downcasting it to find the object back.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-18 15:26:25 +01:00
Sebastien Boeuf
fbd624d816 vmm: device_manager: Remove pci_id_list
Introduces a tuple holding both information needed by pci_id_list and
pci_devices.

Changes pci_devices to be a BTreeMap of this new tuple.

Now that pci_devices holds the information needed from pci_id_list,
pci_id_list is no longer needed.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-18 15:26:25 +01:00
Sebastien Boeuf
305e095d15 vmm: device_manager: Invert pci_id_list HashMap
In anticipation for further factorization, the pci_id_list is now a
hashmap of PCI b/d/f leading to each device name.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-18 15:26:25 +01:00
Sebastien Boeuf
62aaccee28 vmm: Device name verification based on DeviceTree
Instead of relying on a PCI specific device list, we use the DeviceTree
as a reference to determine if a device name is already in use or not.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-18 15:26:25 +01:00
Rob Bradford
13724dbd22 main: Remove messages from startup
Remove the startup message containing incomplete VM configuration
details. It's a bit unusual for a tool such as Cloud Hypervisor to print
those kind of details which are a direct representation of the command
line.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-18 11:32:28 +00:00
Rob Bradford
16cb40733a tests: Extract out generic integration test code
This includes:

* OS disk image management
* Cloud init creation
* SSH to guest access
* Waiting for guest to boot

This will be useful in other projects that want to do similar things in
their integration tests.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-17 12:18:04 +00:00
Rob Bradford
9440304183 vmm: http: Error out earlier if we can't create API server
This removes a panic inside the API thread.

Fixes: #2395

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-17 11:30:26 +00:00
Rob Bradford
78f9ddc6be main: Remove default API sever path
Only create an API server if the use specifies one with `--api-socket`

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-17 11:30:26 +00:00
Rob Bradford
9b0996a71f vmm, main: Optionalise creation of API server
Only if we have a valid API server path then create the API server. For
now this has no functional change there is a default API server path in
the clap handling but rather prepares to do so optionally.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-17 11:30:26 +00:00
Zide Chen
f685f0e0b2 vm-virtio: initialize MSI-X vectors with VIRTIO_MSI_NO_VECTOR
In case of the virtio frontend driver doesn't need interupts for
certain queue event, it may explicitly write VIRTIO_MSI_NO_VECTOR
to the virtio common configuration, or it may doesn't configure
the event type vector at all.

This patch initializes both MSI-X configuration vector and queue vector
with VIRTIO_MSI_NO_VECTOR, so that the backend drivers won't trigger
unexpected interrupts to the guest.

Signed-off-by: Zide Chen <zide.chen@intel.com>
2021-03-17 08:05:24 +01:00
dependabot-preview[bot]
40a3c31df4 build(deps): bump vfio-ioctls from a87b13b to ed8d253
Bumps [vfio-ioctls](https://github.com/rust-vmm/vfio-ioctls) from `a87b13b` to `ed8d253`.
- [Release notes](https://github.com/rust-vmm/vfio-ioctls/releases)
- [Commits](a87b13bdec...ed8d253457)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-17 07:58:11 +01:00
Sebastien Boeuf
04dc496808 ci: Enable baremetal VFIO integration tests
Relying on a NVIDIA Tesla T4 card present in the SGX machine, this patch
enables baremetal VFIO testing, validated by running several NVIDIA
tools in the guest. The guest image has been prepared to include all the
software needed to run these tests.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-16 18:59:46 +00:00
Henry Wang
d0c0a98eda docs: Update the device model documentation
This commit updates the device model documentation with Arm
specific devices.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 20:27:15 +08:00
Henry Wang
205e587fad tests: Enable test_power_button for AArch64
This commit enables the test_power_button test case for AArch64.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 20:27:15 +08:00
Henry Wang
2bb153de2b vmm: Implement the power button method for AArch64
This commit implements the power button method for AArch64 using
the PL061 GPIO controller.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 20:27:15 +08:00
Henry Wang
c853ed3fdc resources: Enable GPIO PL061 driver in kernel
This commit enables the kernel config of PL061 GPIO controller.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 20:27:15 +08:00
Henry Wang
a59ff42a95 aarch64: Add PL061 for device tree implementation
This commit adds a new legacy device PL011 for the AArch64 device
tree implementation.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 20:27:15 +08:00
Penny Zheng
7c86ef8a69 devices: legacy: Add Arm PL061 GPIO controller
This commit implements ARM PrimeCell General Purpose Input/Output
(GPIO) PL061 device specification.

Signed-off-by: Penny Zheng <penny.zheng@arm.com>
Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 20:27:15 +08:00
dependabot-preview[bot]
c7438c8b22 build(deps): bump libc from 0.2.88 to 0.2.89 in /fuzz
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.88 to 0.2.89.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.88...0.2.89)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-16 08:13:22 +00:00
dependabot-preview[bot]
3df2e2629b build(deps): bump io-uring from 0.5.0 to 0.5.1 in /fuzz
Bumps [io-uring](https://github.com/tokio-rs/io-uring) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/tokio-rs/io-uring/releases)
- [Commits](https://github.com/tokio-rs/io-uring/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-16 08:13:10 +00:00
Henry Wang
06d83d2eb3 docs: device_model: Updated the serial device doc
As we switched the AArch64 serial device to PL011, we also need to
update the documentation.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 11:53:51 +08:00
Henry Wang
9c3b489f0e tests: Switch AArch64 serial test cases to ttyAMA0
This commit switches the serial test cases for AArch64 to ttyAMA0.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 11:53:51 +08:00
Henry Wang
3965a8505b resources: AArch64: Enable PL011 driver in kernel
This commit enables the kernel config of PL011 UART controller.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 11:53:51 +08:00
Henry Wang
a8cde12b14 vmm: AArch64: Use PL011 for AArch64 device tree
This commit switches the default serial device from 16550 to the
Arm dedicated UART controller PL011. The `ttyAMA0` can be enabled.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 11:53:51 +08:00
Henry Wang
fd95acc60f devices: legacy: Implement Arm PL011 UART
This commit implements the Arm PrimeCell UART(PL011) device.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2021-03-16 11:53:51 +08:00
dependabot-preview[bot]
c85fba0c43 build(deps): bump libc from 0.2.88 to 0.2.89
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.88 to 0.2.89.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.88...0.2.89)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-15 23:40:01 +00:00
Iggy Jackson
fea1f370ff docs: Add virtiofs root howto
Add a quick HowTo for using a virtiofs root filesystem in a
cloud-hypervisor guest.

Signed-off-by: Iggy Jackson <iggy@theiggy.com>
2021-03-16 00:10:49 +01:00
Bo Chen
6307db5699 virtio-devices: seccomp: Add 'timerfd_settime' to block device
The `timerfd_settime` syscall is required when I/O throttling is
enabled.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-03-15 20:19:33 +00:00
dependabot-preview[bot]
548426c128 build(deps): bump io-uring from 0.5.0 to 0.5.1
Bumps [io-uring](https://github.com/tokio-rs/io-uring) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/tokio-rs/io-uring/releases)
- [Commits](https://github.com/tokio-rs/io-uring/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-15 17:51:01 +00:00
Michael Zhao
c3a75dafc5 tests: Enable serial test cases for AArch64
Enabled all "ttyS0" related test cases:
- test_serial_off
- test_serial_tty
- test_serial_file

Enabled mandatory guest kernel driver for "ns16550a" on AArch64.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2021-03-15 20:59:50 +08:00
Michael Zhao
ee7fcdb3cf aarch64: Correct wrong settings for serial device
Corrected:
- The device name in FDT
- MMIO mapping size

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2021-03-15 20:59:50 +08:00
Michael Zhao
afc83582be aarch64: Enable IRQ routing for legacy devices
On AArch64, interrupt controller (GIC) is emulated by KVM. VMM need to
set IRQ routing for devices, including legacy ones.

Before this commit, IRQ routing was only set for MSI. Legacy routing
entries of type KVM_IRQ_ROUTING_IRQCHIP were missing. That is way legacy
devices (like serial device ttyS0) does not work.

The setting of X86 IRQ routing entries are not impacted.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2021-03-15 20:59:50 +08:00
dependabot-preview[bot]
2a8bdf710d build(deps): bump syn from 1.0.63 to 1.0.64 in /fuzz
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.63 to 1.0.64.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.63...1.0.64)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-15 08:35:57 +00:00
dependabot-preview[bot]
210a9d40c8 build(deps): bump regex from 1.4.4 to 1.4.5
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.4 to 1.4.5.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.4.4...1.4.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-14 22:03:20 +00:00
dependabot-preview[bot]
5bb10adf22 build(deps): bump syn from 1.0.63 to 1.0.64
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.63 to 1.0.64.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.63...1.0.64)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-14 22:03:04 +00:00
dependabot-preview[bot]
97f8f0fb31 build(deps): bump openssl-sys from 0.9.60 to 0.9.61
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.60 to 0.9.61.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.60...openssl-sys-v0.9.61)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-14 22:02:49 +00:00
Rob Bradford
5bc311184e build: Remove url crate dependency
This removes multiple transitive dependencies and speeds up our build.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-12 16:52:55 +01:00
Rob Bradford
7f96eb2b67 vmm: migration: Simplify url socket handling in migration code
Extract URL handling to a common function and simplify to remove url
crate dependency.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-12 16:52:55 +01:00
Rob Bradford
ab4b30edd3 vmm: Switch MemoryManager::send() to url_to_path()
This continues the work in cc78a597cd

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-12 16:52:55 +01:00
Rob Bradford
cc78a597cd vmm: Simplify snapshot/restore path handling
Extend the existing url_to_path() to take the URL string and then use
that to simplify the snapshot/restore code paths.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-12 13:03:01 +01:00
Bo Chen
ebcbab739e vmm: openapi: Add rate_limiter_config to the `DiskConfig
Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-03-12 09:35:03 +01:00
Bo Chen
0c7541473c docs: Add documentation on I/O throttling
Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-03-12 09:35:03 +01:00
Bo Chen
169d6f6756 fuzz: Update the 'block' fuzz target with the rate limiter support
Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-03-12 09:35:03 +01:00
Bo Chen
af8def364d virtio-devices, vmm: add I/O rate limiter on block device
This patch is based on the 'rate_limiter' module from firecracker[1]. To
simplify dependencies, we reply on 'vmm-sys-util::TimerFd' instead of
the `timerfd` crate.

[1]https://github.com/firecracker-microvm/firecracker/tree/master/src/rate_limiter

Fixes: #1285

Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-03-12 09:35:03 +01:00
dependabot-preview[bot]
c45a9a390d build(deps): bump regex from 1.4.3 to 1.4.4
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.3 to 1.4.4.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.4.3...1.4.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-12 08:33:23 +00:00
dependabot-preview[bot]
b5929645c6 build(deps): bump regex-syntax from 0.6.22 to 0.6.23
Bumps [regex-syntax](https://github.com/rust-lang/regex) from 0.6.22 to 0.6.23.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-12 08:33:05 +00:00
Sebastien Boeuf
ec9e6edcd0 virtio-devices: Remove unused update_memory() from VirtioDevice trait
Now that virtio devices can be updated with add_memory_region(), there's
no need to keep update_memory() around.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-11 19:04:21 +01:00
Sebastien Boeuf
00873e5f84 vmm: device_manager: Update virtio devices memory with a single region
Relies on the preliminary work allowing virtio devices to be updated
with a single memory at a time instead of updating the entire memory at
once.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-11 19:04:21 +01:00
Sebastien Boeuf
9e53efa3ca virtio-devices: Add support for adding a single memory region
Assuming vhost-user devices support CONFIGURE_MEM_SLOTS protocol
feature, we introduce a new method to the VirtioDevice trait in order to
update one single memory at a time.

In case CONFIGURE_MEM_SLOTS is not supported by the backend (feature not
acked), we fallback onto the current way of updating the memory
mappings, that is with SET_MEM_TABLE.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-11 19:04:21 +01:00
Sebastien Boeuf
4f22f57651 vhost_user_net: Enable CONFIGURE_MEM_SLOTS protocol feature
In order to support GET_MAX_MEM_SLOTS, ADD_MEM_REG and REM_MEM_REG, the
protocol feature CONFIGURE_MEM_SLOTS must be enabled.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-11 19:04:21 +01:00
Sebastien Boeuf
1e1cbc53c1 vhost_user_blk: Enable CONFIGURE_MEM_SLOTS protocol feature
In order to support GET_MAX_MEM_SLOTS, ADD_MEM_REG and REM_MEM_REG, the
protocol feature CONFIGURE_MEM_SLOTS must be enabled.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-11 19:04:21 +01:00
Sebastien Boeuf
8255d5f774 vhost_user_backend: Move to latest vhost version
The latest vhost version adds the support for the new commands
get_max_mem_slots(), add_mem_region() and remove_mem_region(), all
related to the new vhost-user protocol feature CONFIGURE_MEM_SLOTS.

The vhost_user_backend crate is updated accordingly in order to support
these new commands, mostly related to the capability of updating the
guest memory mappings with a finer control than set_mem_table() command.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-11 19:04:21 +01:00
Sebastien Boeuf
e3a8d6c13c virtio-devices: vhost-user: net: Fix seccomp filters
On x86_64 architecture, multiple syscalls were missing when shutting
down the vhost-user-net device along with the VM. This was causing the
usual crash related to seccomp filters.

This commit adds these missing syscalls to fix the issue.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-11 19:04:21 +01:00
Sebastien Boeuf
581bf4aad5 virtio-devices: vhost-user: Fix device reset
There is no need to get the vring base when resetting the vhost-user
device. This was mostly ignored, but in some cases, it was causing some
actual errors.

A reset must simply be a combination of disabling the vrings along with
the reset of the owner.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-11 19:04:21 +01:00
Jiachen Zhang
16b82f3dfe vhost-user: Make ActivateError messages more consistent
Originally, VhostUserSetup is only used by vhost-user-fs. While
vhost-user-blk and vhost-user-net have their own error messages,
we rename VhostUserSetup to VhostUserFsSetup.

Signed-off-by: Jiachen Zhang <zhangjiachen.jaycee@bytedance.com>
2021-03-11 15:09:07 +00:00
dependabot-preview[bot]
675a8f808c build(deps): bump signal-hook from 0.3.6 to 0.3.7 in /fuzz
Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.3.6 to 0.3.7.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-11 09:29:26 +00:00
dependabot-preview[bot]
59e5048436 build(deps): bump byteorder from 1.4.2 to 1.4.3 in /fuzz
Bumps [byteorder](https://github.com/BurntSushi/byteorder) from 1.4.2 to 1.4.3.
- [Release notes](https://github.com/BurntSushi/byteorder/releases)
- [Changelog](https://github.com/BurntSushi/byteorder/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/byteorder/compare/1.4.2...1.4.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-11 08:45:59 +00:00
dependabot-preview[bot]
7b3e79cdfa build(deps): bump signal-hook from 0.3.6 to 0.3.7
Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.3.6 to 0.3.7.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-11 08:45:27 +00:00
Rob Bradford
66ffaceffc vmm: tdx: Correctly populate the 64-bit MMIO region
The MMIO structure contains the length rather than the maximum address
so it is necessary to subtract the starting address from the end address
to calculate the length.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-10 14:45:51 +00:00
dependabot-preview[bot]
4e307788b7 build(deps): bump byteorder from 1.4.2 to 1.4.3
Bumps [byteorder](https://github.com/BurntSushi/byteorder) from 1.4.2 to 1.4.3.
- [Release notes](https://github.com/BurntSushi/byteorder/releases)
- [Changelog](https://github.com/BurntSushi/byteorder/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/byteorder/compare/1.4.2...1.4.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-10 13:14:58 +00:00
Muminul Islam
8739f6cccb scripts: Fix windows test script for running on MSHV
In order to run Windows test on MSHV we need to build and test using
MSHV feature.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-03-10 10:08:12 +00:00
dependabot-preview[bot]
5599cbef50 build(deps): bump syn from 1.0.62 to 1.0.63 in /fuzz
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.62 to 1.0.63.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.62...1.0.63)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-10 08:03:10 +00:00
dependabot-preview[bot]
4c52ad2550 build(deps): bump syn from 1.0.62 to 1.0.63
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.62 to 1.0.63.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.62...1.0.63)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-10 08:02:59 +00:00
Rob Bradford
a0c07474a3 vmm: seccomp: Add KVM_MEMORY_ENCRYPT_OP ioctl to seccomp filter
This is the basis for TDX based operations on the various KVM file
descriptors.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-09 16:26:06 +01:00
Rob Bradford
be0cbb09b1 build: Clippy check with "tdx" feature
In the absence of a way of integration testing this testing that it
compiles is reasonable compromise.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
d24aa887b6 vmm: Reject VM snapshot request if TDX in use
It is not possible to snapshot the contents of a TDX VM.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
835a31e283 vmm: config: Require max and boot vCPUs to be equal for TDX
CPU hotplug is not possible with TDX

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
57c8c250fd tdx: Permit starting Cloud Hypervisor without --kernel
This is not required if TDX is present.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
62abc117ab tdx: Configure TDX state for the VM
Load the sections backed from the file into their required addresses in
memory and populate the HOB with details of the memory. Using the HOB
address initialize the TDX state in the vCPUs and finalize the TDX
configuration.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
1c54fc3ab7 hypervisor: Support creating a VM of a specified KVM type
This is necessary to support creating a TD VM.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
57ce0986f7 vmm: cpu: Add functionality for enabling TDX for all vCPUs
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
c8cad394b5 vmm: cpu: Expose the common/shared CPUID data for all vCPUs
This allows the CPUID data to be passed into the VM level ioctl used for
initalizing TDX.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
b02aff5761 vmm: memory_manager: Disable dirty page logging when running on TDX
It is not permitted to have this enabled in memory that is part of a TD.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
f282cc001a tdx: Add abstraction to call TDX ioctls to hypervisor
Add API to the hypervisor interface and implement for KVM to allow the
special TDX KVM ioctls on the VM and vCPU FDs.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
c48e82915d vmm: Make kernel optional in VM internals
When booting with TDX no kernel is supplied as the TDFV is responsible
for loading the OS. The requirement to have the kernel is still
currently enforced at the validation entry point; this change merely
changes function prototypes and stored state to use Option<> to support.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
66a3bed086 vmm: config: Add "--tdx" option parsing
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
45cc26f940 tdx: Add support for generating a TD HOB list
This is used to communicate details of the memory configuration from the
VMM into the TDMF.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
77955bd8f9 tdx: Add support for parsing TDVF metadata
Add support extracting the sections out for a TDVF file which can be
then used to load the TDVF and TD HOB data into their appropriate
locations.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Rob Bradford
e61ee6bcac tdx: Add "tdx" feature with an empty module inside arch to implement
Add the skeleton of the "tdx" feature with a module ready inside the
arch crate to store implementation details.

TEST=cargo build --features="tdx"

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 18:30:00 +00:00
Vineeth Pillai
fd9bd1c86c main: minor fix in the help message for event monitor
Signed-off-by: Vineeth Pillai <viremana@linux.microsoft.com>
2021-03-08 15:32:18 +00:00
Rob Bradford
cf9e81c05a build: Update kvm-ioctls and kvm-bindings dependencies
These need to be updated together as the kvm-ioctls depends upon a
strictly newer version of kvm-bindings which requires a rebase in the CH
fork.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-08 14:52:30 +00:00
dependabot-preview[bot]
a4a2d6fbee build(deps): bump syn from 1.0.61 to 1.0.62 in /fuzz
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.61 to 1.0.62.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.61...1.0.62)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-08 08:56:23 +00:00
dependabot-preview[bot]
94083793a6 build(deps): bump serde from 1.0.123 to 1.0.124 in /fuzz
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.123 to 1.0.124.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.123...v1.0.124)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-08 08:46:07 +00:00
dependabot-preview[bot]
8ae966e975 build(deps): bump syn from 1.0.61 to 1.0.62
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.61 to 1.0.62.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.61...1.0.62)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-08 07:46:17 +00:00
dependabot-preview[bot]
f68fe54b9b build(deps): bump vhost from 62fd4ec to 7784304
Bumps [vhost](https://github.com/rust-vmm/vhost) from `62fd4ec` to `7784304`.
- [Release notes](https://github.com/rust-vmm/vhost/releases)
- [Commits](62fd4ec5a4...7784304860)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-08 07:45:52 +00:00
dependabot-preview[bot]
54e1796da6 build(deps): bump serde from 1.0.123 to 1.0.124
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.123 to 1.0.124.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.123...v1.0.124)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-07 23:09:32 +00:00
dependabot-preview[bot]
c8be08adf6 build(deps): bump serde_derive from 1.0.123 to 1.0.124
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.123 to 1.0.124.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.123...v1.0.124)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-07 21:54:56 +00:00
dependabot-preview[bot]
ccfa34d066 build(deps): bump libc from 0.2.87 to 0.2.88
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.87 to 0.2.88.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.87...0.2.88)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-05 18:39:37 +00:00
Sebastien Boeuf
d65a0b68b9 Revert "Jenkinsfile: Temporarily disable SGX CI"
This reverts commit 526cf32a78.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-05 19:02:30 +01:00
William Douglas
56028fb214 Try to restore pty configuration on reboot
When a vm is created with a pty device, on reboot the pty fd (sub
only) will only be associated with the vmm through the epoll event
loop. The fd being polled will have been closed due to the vm itself
dropping the pty files (and potentially reopening the fd index to a
different item making things quite confusing) and new pty fds will be
opened but not polled on for input.

This change creates a structure to encapsulate the information about
the pty fd (main File, sub File and the path to the sub File). On
reboot, a copy of the console and serial pty structs is then passed
down to the new Vm  instance which will be used instead of creating a
new pty device.

This resolves the underlying issue from #2316.

Signed-off-by: William Douglas <william.r.douglas@gmail.com>
2021-03-05 18:34:52 +01:00
Sebastien Boeuf
933d41cf2f vmm: Provide DMA mapping handlers to virtio-mem devices
Now that virtio-mem devices can update VFIO mappings through dedicated
handlers, let's provide them from the DeviceManager.

Important to note these handlers should either be provided to virtio-mem
devices or to the unique virtio-iommu device. This must be mutually
exclusive.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-05 10:38:42 +01:00
Sebastien Boeuf
61f9a4ec6c virtio-devices: mem: Accept handlers to update DMA mappings
Create two functions for registering/unregistering DMA mapping handlers,
each handler being associated with a VFIO device.

Whenever the plugged_size is modified (which means triggered by the
virtio-mem driver in the guest), the virtio-mem backend is responsible
for updating the DMA mappings related to every VFIO device through the
handler previously provided.

It's important to update the map when the handler is either registered
or unregistered as well, as we don't want to miss some plugged memory
that would have been added before the VFIO device is added to the VM.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-05 10:38:42 +01:00
Sebastien Boeuf
080ea31813 pci, vmm: Manage VFIO DMA mapping from DeviceManager
Instead of letting the VfioPciDevice take the decision on how/when to
perform the DMA mapping/unmapping, we move this to the DeviceManager
instead.

The point is to let the DeviceManager choose which guest memory regions
should be mapped or not. In particular, we don't want the virtio-mem
region to be mapped/unmapped as it will be virtio-mem device
responsibility to do so.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-05 10:38:42 +01:00
Sebastien Boeuf
d6db2fdf96 vmm: memory_manager: Add ACPI hotplug region to default memory zone
When memory is resized through ACPI, a new region is added to the guest
memory. This region must also be added to the corresponding memory zone
in order to keep everything in sync.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-05 10:38:42 +01:00
dependabot-preview[bot]
f1aa09f178 build(deps): bump syn from 1.0.60 to 1.0.61 in /fuzz
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.60 to 1.0.61.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.60...1.0.61)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-05 07:49:52 +00:00
dependabot-preview[bot]
8cbb7b15a9 build(deps): bump syn from 1.0.60 to 1.0.61
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.60 to 1.0.61.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.60...1.0.61)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-05 07:14:58 +00:00
Rob Bradford
b65502c3c1 main: Refine event monitor control
Replace "--monitor-fd" with "--event-monitor" which can either take
"fd=<int>" or "path=<path>" which can point to e.g. a named pipe and
allow more flexibility.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-04 19:12:40 +01:00
dependabot-preview[bot]
3ef7c8eac5 build(deps): bump vhost from 576694b to 62fd4ec
Bumps [vhost](https://github.com/rust-vmm/vhost) from `576694b` to `62fd4ec`.
- [Release notes](https://github.com/rust-vmm/vhost/releases)
- [Commits](576694bcfb...62fd4ec5a4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-04 08:38:19 +00:00
Sebastien Boeuf
526cf32a78 Jenkinsfile: Temporarily disable SGX CI
Since the SGX server is down for maintenance, all builds are waiting on
the node agent to answer, causing all PRs to be blocked.

Let's disable temporarily the SGX CI until the server is back up.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-03 09:56:05 +00:00
dependabot-preview[bot]
b0ddc5bd60 build(deps): bump libc from 0.2.86 to 0.2.87 in /fuzz
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.86 to 0.2.87.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.86...0.2.87)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-03 10:21:43 +01:00
dependabot-preview[bot]
b72f6046e5 build(deps): bump vfio-ioctls from 0903c22 to a87b13b
Bumps [vfio-ioctls](https://github.com/rust-vmm/vfio-ioctls) from `0903c22` to `a87b13b`.
- [Release notes](https://github.com/rust-vmm/vfio-ioctls/releases)
- [Commits](0903c222fa...a87b13bdec)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-03 10:21:17 +01:00
dependabot-preview[bot]
8a2bd01e25 build(deps): bump once_cell from 1.7.1 to 1.7.2
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.7.1 to 1.7.2.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.7.1...v1.7.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-02 15:53:53 +00:00
dependabot-preview[bot]
d433ae1656 build(deps): bump libc from 0.2.86 to 0.2.87
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.86 to 0.2.87.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.86...0.2.87)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-02 11:14:57 +00:00
dependabot-preview[bot]
0348f480fa build(deps): bump once_cell from 1.7.0 to 1.7.1
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.7.0...v1.7.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-02 09:24:39 +00:00
Wei Liu
74565538ae hypervisor: mshv: hook up TranslateGVA hypercall
At this stage this is the bare minimum needed to make Windows server
2019 work on MSHV.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-03-02 07:08:36 +01:00
Rob Bradford
99baee8d37 tests: Move Windows integration test to updated OVMF
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-03-01 22:11:19 +00:00
Wei Liu
030a86db17 hypervisor: mshv: simplify GVA to GPA cache
So far we've only had the need to emulate one instruction. There is no
need to use a HashMap when a simple tuple for the initial mapping will
do.

We can bring back the HashMap once more sophisticated use cases surface.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-03-01 17:50:47 +00:00
Wei Liu
3eb5b67dc3 hypervisor: mshv: make SoftTLB part of MshvEmulatorContext
This avoids code complexity down the line when we get around
implementing Windows support.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-03-01 17:50:47 +00:00
Sebastien Boeuf
c27d6df233 vhost: Bump to latest version from upstream
Moving to the latest version of the rust-vmm/vhost crate, before it gets
published on crates.io.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-01 15:53:46 +01:00
dependabot-preview[bot]
30cd3cb764 deps: bump io-uring from 0.4.0 to 0.5.0
Bumps [io-uring](https://github.com/tokio-rs/io-uring) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/tokio-rs/io-uring/releases)
- [Commits](https://github.com/tokio-rs/io-uring/commits)

The API was changed, hence some changes were needed to keep the code
building and functional.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-03-01 11:08:25 +00:00
dependabot-preview[bot]
e31c2be60a build(deps): bump serde_json from 1.0.63 to 1.0.64
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.63 to 1.0.64.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.63...v1.0.64)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-28 18:35:17 +00:00
dependabot-preview[bot]
fb853a50b1 build(deps): bump serde_json from 1.0.62 to 1.0.63
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.62 to 1.0.63.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.62...v1.0.63)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-26 13:00:32 +00:00
dependabot-preview[bot]
8c38d9576c build(deps): bump adler from 1.0.1 to 1.0.2
Bumps [adler](https://github.com/jonas-schievink/adler) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/jonas-schievink/adler/releases)
- [Changelog](https://github.com/jonas-schievink/adler/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jonas-schievink/adler/compare/v1.0.1...v1.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-26 12:46:18 +00:00
dependabot-preview[bot]
5e98810c2d build(deps): bump libfuzzer-sys from 0.3.5 to 0.4.0 in /fuzz
Bumps [libfuzzer-sys](https://github.com/rust-fuzz/libfuzzer) from 0.3.5 to 0.4.0.
- [Release notes](https://github.com/rust-fuzz/libfuzzer/releases)
- [Changelog](https://github.com/rust-fuzz/libfuzzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-fuzz/libfuzzer/commits/0.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-26 12:07:51 +00:00
Rob Bradford
f8875acec2 misc: Bulk upgrade dependencies
In particular update for the vmm-sys-util upgrade and all the other
dependent packages. This requires an updated forked version of
kvm-bindings (due to updated vfio-ioctls) but allowed the removal of our
forked version of kvm-ioctls.

The changes to the API from kvm-ioctls and vmm-sys-util required some
other minor changes to the code.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-26 11:31:08 +00:00
Sebastien Boeuf
fa8fcf5f4c vhost: Move to upstream crate
The vhost crate from rust-vmm is ready, which is why we do the switch
from the Cloud Hypervisor fork to the upstream crate.

At the same time, we rename the crate from vhost_rs to vhost.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-25 11:20:41 +01:00
dependabot-preview[bot]
3e847112db build(deps): bump once_cell from 1.6.0 to 1.7.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.6.0...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-24 19:43:07 +00:00
Sebastien Boeuf
a0a89b1346 pci, vmm: Move to upstream vfio-ioctls crate
This commit moves both pci and vmm code from the internal vfio-ioctls
crate to the upstream one from the rust-vmm project.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-24 08:02:37 +01:00
Sebastien Boeuf
aee1155870 virtio-devices, vmm: Move to ExternalDmaMapping from vm-device
Now that ExternalDmaMapping is defined in vm-device, let's use it from
there.

This commit also defines the function get_host_address_range() to move
away from the vfio-ioctls dependency.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-24 08:02:37 +01:00
Sebastien Boeuf
5bd05b1af0 vm-device: Move ExternalDmaMapping trait out of vfio-ioctls
By moving the trait and its VFIO implementation out of vfio-ioctls, we
give anticipate for the move to the vfio-ioctls from rust-vmm.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-24 08:02:37 +01:00
Rob Bradford
727287b69d build: Drop aarch64 cross-build
This has been lagging behind on an older Rust version and we have
enough coverage from our Jenkins CI agent.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-23 11:07:48 +00:00
Rob Bradford
deedfcdc35 vmm: Improve restore error message about URL conversion
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-23 11:07:48 +00:00
Rob Bradford
2122233047 build: Remove "wait-timeout" dependency to dev-dependencies
This is only use in the integration test and was erroneously included in
the main binary dependencies.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-23 11:07:48 +00:00
Rob Bradford
afce21ba59 arch: Run interrupt tests
The interrupt tests were not being run as they were erroneously under a
feature guard that does not exist in arch.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-23 11:07:48 +00:00
Rob Bradford
ade5097878 arch: use libc::getrandom() instead of rand crate
This removes the last use of rand in our tree and the removal of several
dependencies.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-23 11:07:48 +00:00
Rob Bradford
24922ce1e3 tests: Move integration tests to vmm_sys_util::tempdir::TempDir
This removes the dependency on "tempdir" which in turn depends on the
large rand dependency chain.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-23 11:07:48 +00:00
Rob Bradford
c1be41bfbf net_util: Replace use of rand crate with libc::getrandom()
The rand crate provides a full cross platform true cryptographic random
number implementation. As such it brings it lots of othe dependencies
and increases our binary size and compile time. This is excessive for
generating a MAC address.

From the cargo tree output:

│   │   ├── rand v0.8.3
│   │   │   ├── libc v0.2.86
│   │   │   ├── rand_chacha v0.3.0
│   │   │   │   ├── ppv-lite86 v0.2.10
│   │   │   │   └── rand_core v0.6.0
│   │   │   │       └── getrandom v0.2.0
│   │   │   │           ├── cfg-if v0.1.10
│   │   │   │           └── libc v0.2.86
│   │   │   └── rand_core v0.6.0 (*)

And cargo bloat:

 0.0%   0.4% 40.4KiB rand_chacha rand_chacha::guts::refill_wide::impl_sse2
 0.0%   0.4% 40.0KiB rand_chacha rand_chacha::guts::refill_wide::impl_ssse3
 0.0%   0.3% 37.6KiB rand_chacha rand_chacha::guts::refill_wide::impl_avx
 0.0%   0.3% 37.2KiB rand_chacha rand_chacha::guts::refill_wide::impl_sse41
 0.0%   0.2% 26.1KiB rand_chacha rand_chacha::guts::refill_wide::impl_avx2

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-23 11:07:48 +00:00
Bo Chen
d361fc1a36 vmm: config: Fix and complete the help info for the '--disk' option
The help information displayed for our `--disk` option is incorrect and
incomplete, e.g. missing the `direct` and `poll_queue` field.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-02-23 08:55:33 +01:00
Wei Liu
49214cf02b hypervisor: emulator: fix MOVZX
According to Intel's mnemonic (which is used by iced-x86) the first
argument is destination while the second is source.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-23 07:42:33 +01:00
Rob Bradford
d78b2ec8b5 tests: Use vmm_sys_util::tempfile::Tempfile in integration tests
This removes the requirement for an extra crate and simplifies the
dependency chain.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-22 14:29:53 +01:00
Rob Bradford
946c484590 devices: Remove dependency on tempfile crate
This was completely unused.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-22 14:29:53 +01:00
Rob Bradford
cd700bf449 virtio-devices: Remove dependency on tempfile crate
This was completely unused.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-22 14:29:53 +01:00
Rob Bradford
cf7a05ecb5 block_util: Use vmm_sys_util::tempfile::Tempfile
This removes the requirement for an extra crate.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-22 14:29:53 +01:00
Rob Bradford
0497a7c311 qcow: Use vmm_sys_util::tempfile::Tempfile
This removes the requirement for an extra crate.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-22 14:29:53 +01:00
Rob Bradford
05a2b3fac2 vmm: Remove "tempfile" dependency from vmm
This was completely unused.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-22 14:29:53 +01:00
dependabot-preview[bot]
d33c0563af build(deps): bump once_cell from 1.5.2 to 1.6.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.5.2 to 1.6.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.5.2...v1.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-22 13:28:54 +00:00
Sebastien Boeuf
4ed0e1a3c8 net_util: Simplify TX/RX queue handling
The main idea behind this commit is to remove all the complexity
associated with TX/RX handling for virtio-net. By using writev() and
readv() syscalls, we could get rid of intermediate buffers for both
queues.

The complexity regarding the TAP registration has been simplified as
well. The RX queue is only processed when some data are ready to be
read from TAP. The event related to the RX queue getting more
descriptors only serves the purpose to register the TAP file if it's not
already.

With all these simplifications, the code is more readable but more
performant as well. We can see an improvement of 10% for a single
queue device.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-22 10:39:23 +00:00
dependabot-preview[bot]
5ed2a654e8 build(deps): bump generator from 0.6.23 to 0.6.24
Bumps [generator](https://github.com/Xudong-Huang/generator-rs) from 0.6.23 to 0.6.24.
- [Release notes](https://github.com/Xudong-Huang/generator-rs/releases)
- [Commits](https://github.com/Xudong-Huang/generator-rs/compare/0.6.23...0.6.24)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-22 06:30:32 +00:00
dependabot-preview[bot]
ae04fe432c build(deps): bump signal-hook from 0.3.4 to 0.3.6
Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.3.4 to 0.3.6.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/compare/v0.3.4...v0.3.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-21 09:36:30 +00:00
dependabot-preview[bot]
c8d142eb55 build(deps): bump crossbeam-utils from 0.8.1 to 0.8.2
Bumps [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) from 0.8.1 to 0.8.2.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-utils-0.8.1...crossbeam-utils-0.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-21 09:36:12 +00:00
dependabot-preview[bot]
8533d63514 build(deps): bump cc from 1.0.66 to 1.0.67
Bumps [cc](https://github.com/alexcrichton/cc-rs) from 1.0.66 to 1.0.67.
- [Release notes](https://github.com/alexcrichton/cc-rs/releases)
- [Commits](https://github.com/alexcrichton/cc-rs/compare/1.0.66...1.0.67)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-19 22:54:50 +00:00
Muminul Islam
0ef69fa592 tests: Use constant instead of static value for windows image name
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-02-19 10:09:23 +00:00
Muminul Islam
29f924405a scripts: Check if windows image is in the host
Currently script does not exit early if the image/firmware not present
in the host. We should not progress further if the images are not pre
downloaded.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-02-19 10:09:23 +00:00
Muminul Islam
282134a490 scripts: Use variable for with windows image file
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-02-19 10:09:23 +00:00
dependabot-preview[bot]
1fbdca16bf build(deps): bump form_urlencoded from 1.0.0 to 1.0.1
Bumps [form_urlencoded](https://github.com/servo/rust-url) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v1.0.0...percent-encoding-v1.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-19 09:53:49 +00:00
Rob Bradford
32a2000ecc build: Only build the {kvm,mshv}-{ioctls,bindings} needed
This simplifies the Cloud Hypervisor dependency chain slightly.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-19 08:27:30 +01:00
dependabot-preview[bot]
6a499f02d9 build(deps): bump linux-loader from 2855be1 to 4ab9dad
Bumps [linux-loader](https://github.com/rust-vmm/linux-loader) from `2855be1` to `4ab9dad`.
- [Release notes](https://github.com/rust-vmm/linux-loader/releases)
- [Commits](2855be15a7...4ab9dade2c)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-19 07:14:55 +00:00
dependabot-preview[bot]
0a9c052ebd build(deps): bump thiserror from 1.0.23 to 1.0.24
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.23 to 1.0.24.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.23...1.0.24)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-19 07:14:39 +00:00
dependabot-preview[bot]
bfb12b7777 build(deps): bump url from 2.2.0 to 2.2.1
Bumps [url](https://github.com/servo/rust-url) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.2.0...v2.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-18 21:56:54 +00:00
Rob Bradford
c89095ab85 virtio-devices: Report events for virtio device activation and reset
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-18 16:15:13 +00:00
Rob Bradford
9260c4c10e vmm: Use event!() for some key VM actions
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-18 16:15:13 +00:00
Rob Bradford
4822ed79e1 main: Add "--monitor-fd" to write structured event data to
If supplied then structured JSON event data will be written to that file
descriptor.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-18 16:15:13 +00:00
Rob Bradford
ddbef7450d event_monitor: Add new crate for event reporting
This crate exposes the abililty for the VMM to set a file that events
should be written to. The event!() macro provides an interface to report
those events allowing the specification of an event source, an event
type and optional extra data. This will be written to the provided file
descriptor as JSON data.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-18 16:15:13 +00:00
Rob Bradford
c1d9edbfc0 vmm: seccomp: Add getrandom to vCPU thread filter
This can be triggered upon device reset.

Fixes: #2278

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-18 16:15:13 +00:00
dependabot-preview[bot]
0d209e135e build(deps): bump idna from 0.2.1 to 0.2.2
Bumps [idna](https://github.com/servo/rust-url) from 0.2.1 to 0.2.2.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v0.2.1...idna-v0.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-18 09:26:35 +00:00
Wei Liu
e22b6ec768 hypervisor: x86: emulate MOVS instruction
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-17 09:36:19 +01:00
Wei Liu
b59243f6cf hypervisor: mshv: support reading and writing guest memory in emulator
We don't have an easy way to figure out if a GPA points to normal memory
or device memory, but the guest's normal memory regions shouldn't
overlap with device regions. We can simply try to do a normal memory
read / write, and proceed to do device memory read / write if that
fails.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-17 09:36:19 +01:00
Rob Bradford
07a09eda27 hypervisor: kvm: Remove whitespace from use statements
This allows cargo fmt to correctly order the statements.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-16 18:38:57 +01:00
Rob Bradford
38c41a5074 vmm: memory_manager: Extract code for allocating new memory
This function can then be used by the TDX code to allocate the memory at
specific locations required for the TDVF to run from.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-16 18:38:57 +01:00
Rob Bradford
6e4c90f305 arch: Include "thiserror" crate as well as "anyhow"
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-16 18:38:57 +01:00
Rob Bradford
707bb0ba72 vmm: Simplify return path of vm_boot
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-16 18:38:57 +01:00
Wei Liu
29881a2d6a hypervisor: mshv: explicitly skip a few IO ports
OVMF would use string IO on those ports. String IO has not been
implemented, so that leads to panics.

Skip them explicitly in MSHV. Leave a long-ish comment in code to
explain the situation. We should properly implement string IO once it
becomes feasible / necessary.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-16 10:04:58 +01:00
Rob Bradford
a330a1569a arch, arch_gen, hypervisor: Remove some unnecessary clippy attributes
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-15 18:03:27 +01:00
Wei Liu
cf6480f012 hypervisor: mshv: drop some clippy attributes
They were used to suppress warnings during development. At this stage
they aren't needed anymore.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-15 14:52:26 +01:00
Bo Chen
a838d0bde1 scripts, tests: Rustify the network setup commands for tests
Fixes: #2218

Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-02-15 08:34:23 +00:00
Rob Bradford
f58b6f8417 build, release-notes.md, rpm: Release 0.13.0
Update version numbers and release notes for new release.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-12 10:38:48 +00:00
dependabot-preview[bot]
71ae9cfe06 build(deps): bump quote from 1.0.8 to 1.0.9
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.8 to 1.0.9.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.8...1.0.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-12 08:16:19 +00:00
dependabot-preview[bot]
f0b7a09a49 build(deps): bump env_logger from 0.8.2 to 0.8.3
Bumps [env_logger](https://github.com/env-logger-rs/env_logger) from 0.8.2 to 0.8.3.
- [Release notes](https://github.com/env-logger-rs/env_logger/releases)
- [Changelog](https://github.com/env-logger-rs/env_logger/blob/master/CHANGELOG.md)
- [Commits](https://github.com/env-logger-rs/env_logger/compare/v0.8.2...v0.8.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-12 08:15:55 +00:00
Rob Bradford
bc84a1c79b build: Remove nested matches
Update for clippy in Rust 1.50.0:

error: Unnecessary nested match
   --> vmm/src/vm.rs:419:17
    |
419 | /                 if let vm_device::BusError::MissingAddressRange = e {
420 | |                     warn!("Guest MMIO write to unregistered address 0x{:x}", gpa);
421 | |                 }
    | |_________________^
    |
    = note: `-D clippy::collapsible-match` implied by `-D warnings`
help: The outer pattern can be modified to include the inner pattern.
   --> vmm/src/vm.rs:418:17
    |
418 |             Err(e) => {
    |                 ^ Replace this binding
419 |                 if let vm_device::BusError::MissingAddressRange = e {
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-11 18:18:44 +00:00
Rob Bradford
9c5be6f660 build: Remove unnecessary Result<> returns
If the function can never return an error this is now a clippy failure:

error: this function's return value is unnecessarily wrapped by `Result`
   --> virtio-devices/src/watchdog.rs:215:5
    |
215 | /     fn set_state(&mut self, state: &WatchdogState) -> io::Result<()> {
216 | |         self.common.avail_features = state.avail_features;
217 | |         self.common.acked_features = state.acked_features;
218 | |         // When restoring enable the watchdog if it was previously enabled. We reset the timer
...   |
223 | |         Ok(())
224 | |     }
    | |_____^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-11 18:18:44 +00:00
Rob Bradford
8e9a5a0dbb vm-migration: Use vm-memory's ByteValued instead of own AsBytes
This addresses a newly added clippy issue:

error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
  --> vm-migration/src/protocol.rs:57:34
   |
57 |     fn as_mut_bytes<T: Sized>(p: &mut T) -> &mut [u8] {
   |                                  ^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention

error: aborting due to 2 previous errors

error: could not compile `vm-migration`

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-11 18:18:44 +00:00
Bo Chen
d561b45552 tests: Add integration test test_macvtap
This integration test mimics the instructions from
`docs/macvtap-bridge.md`.

Fixes: #2215

Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-02-11 15:25:32 +00:00
Sebastien Boeuf
9353856426 vmm: Fix seccomp filters for vCPUs
Depending on the host OS the code for looking up the time for the CMOS
make require extra syscalls to be permitted for the vCPU thread.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-11 11:24:57 +00:00
Sebastien Boeuf
9af477e964 pci: vfio: Check VFIO device interrupt's support
In case the VFIO device does not support MSI or MSI-X, the capabilities
should not be parsed, avoiding the exposure of unsupported capabilities.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Sebastien Boeuf
27515a6ec4 pci: vfio: Propagate errors when enabling interrupts
Make sure to propagate the error coming from VfioDevice when trying to
enable INTx, MSI or MSI-X interrutps.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Sebastien Boeuf
169a6bb83b docs: vfio: Remove documentation regarding VFIO limitations
Since INTx is now supported for VFIO PCI devices, there is no more
limitations regarding VFIO, which means the documentation must be
updated accordingly.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Sebastien Boeuf
19167e7647 pci: vfio: Implement INTx support
With all the preliminary work done in the previous commits, we can
update the VFIO implementation to support INTx along with MSI and MSI-X.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Sebastien Boeuf
2cdc3e3546 vmm: device_manager: Add PCI routing table to ACPI
Here we are adding the PCI routing table, commonly called _PRT, to the
ACPI DSDT. For simplification reasons, we chose not to implement PCI
links as this involves dynamic decision from the guest OS, which result
in lots of complexity both from an AML perspective and from a device
manager perspective.

That's why the _PRT creates a static list of 32 entries, each assigned
with the IRQ number previously reserved by the device manager.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Sebastien Boeuf
de9471fc72 vmm: device_manager: Allocate IRQs for PCI devices
In order to support INTx for PCI devices, each PCI device must be
assigned an IRQ. This is preliminary work to reserve 8 IRQs which will
be shared across the 32 PCI devices.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Sebastien Boeuf
b5450ca72c vmm: device_manager: Store legacy interrupt manager
In anticipation for accessing the legacy interrupt manager from the
function creating a VFIO PCI device, we store it as part of the
DeviceManager, to make it available for all methods.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Sebastien Boeuf
8008aad545 vmm: device_manager: Don't pass the MSI interrupt manager around
The DeviceManager already has a hold onto the MSI interrupt manager,
therefore there's no need to pass it through every function. Instead,
let's simplify the code by using the attribute from DeviceManager's
instance.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Sebastien Boeuf
3bd47ffdc1 interrupt: Add a notifier method to the InterruptController
Both GIC and IOAPIC must implement a new method notifier() in order to
provide the caller with an EventFd corresponding to the IRQ it refers
to.

This is needed in anticipation for supporting INTx with VFIO PCI
devices.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Sebastien Boeuf
acfbee5b7a interrupt: Make notifier function return Option<EventFd>
In anticipation for supporting the notifier function for the legacy
interrupt source group, we need this function to return an EventFd
instead of a reference to this same EventFd.

The reason is we can't return a reference when there's an Arc<Mutex<>>
involved in the call chain.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-10 17:34:56 +00:00
Wei Liu
62c6efb2e5 build: fix cargo fuzz build
The dependency on vm-memory was not updated by dependabot.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-10 14:42:41 +01:00
Wei Liu
aec0d309ee hypervisor: emulator: also set ES segment in MockVMM
Some instructions, such as MOVS, need a valid ES segment.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-10 11:31:03 +00:00
Wei Liu
5fc12862e6 hypervisor, vmm: minor changes to VmmOps
Swap the last two parameters of guest_mem_{read,write} to be consistent
with other read / write functions.

Use more descriptive parameter names.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-10 11:31:03 +00:00
Wei Liu
f4159ca016 hypervisor: emulator: drop useless set_ip calls
The instruction pointer is already pointing at the instruction being
emulated.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-10 11:31:03 +00:00
dependabot-preview[bot]
6d63018d9f build(deps): bump vm-memory from 0.4.0 to 0.5.0
Bumps [vm-memory](https://github.com/rust-vmm/vm-memory) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/rust-vmm/vm-memory/releases)
- [Changelog](https://github.com/rust-vmm/vm-memory/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-vmm/vm-memory/compare/v0.4.0...v0.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-10 11:30:05 +00:00
dependabot-preview[bot]
073e0f979b build(deps): bump vfio-ioctls from 49cc362 to 6ea1b93
Bumps [vfio-ioctls](https://github.com/cloud-hypervisor/vfio-ioctls) from `49cc362` to `6ea1b93`.
- [Release notes](https://github.com/cloud-hypervisor/vfio-ioctls/releases)
- [Commits](49cc3626f6...6ea1b934bb)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-10 07:01:43 +00:00
Rob Bradford
50a995b63d vmm: Rename patch_cpuid() to generate_common_cpuid()
This reflects that it generates CPUID state used across all vCPUs.
Further ensure that errors from this function get correctly propagated.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-09 16:02:25 +00:00
Rob Bradford
ccdea0274c vmm, arch: Move KVM HyperV emulation handling to shared CPUID code
Move the code for populating the CPUID with KVM HyperV emulation details from
the per-vCPU CPUID handling code to the shared CPUID handling code.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-09 16:02:25 +00:00
Rob Bradford
688ead51c6 vmm, arch: Move CPU identification handling to shared CPUID code
Move the code for populating the CPUID with details of the CPU
identification from the per-vCPU CPUID handling code to the shared CPUID
handling code.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-09 16:02:25 +00:00
Rob Bradford
9792c9aafa vmm, arch: Move max_phys_bits handling to shared CPUID code
Move the code for populating the CPUID with details of the maximum
address space from the per-vCPU CPUID handling code to the shared CPUID
handling code.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-09 16:02:25 +00:00
William Douglas
48963e322a Enable pty console
Add the ability for cloud-hypervisor to create, manage and monitor a
pty for serial and/or console I/O from a user. The reasoning for
having cloud-hypervisor create the ptys is so that clients, libvirt
for example, could exit and later re-open the pty without causing I/O
issues. If the clients were responsible for creating the pty, when
they exit the main pty fd would close and cause cloud-hypervisor to
get I/O errors on writes.

Ideally the main and subordinate pty fds would be kept in the main
vmm's Vm structure. However, because the device manager owns parsing
the configuration for the serial and console devices, the information
is instead stored in new fields under the DeviceManager structure
directly.

From there hooking up the main fd is intended to look as close to
handling stdin and stdout on the tty as possible (there is some future
work ahead for perhaps moving support for the pty into the
vmm_sys_utils crate).

The main fd is used for reading user input and writing to output of
the Vm device. The subordinate fd is used to setup raw mode and it is
kept open in order to avoid I/O errors when clients open and close the
pty device.

The ability to handle multiple inputs as part of this change is
intentional. The current code allows serial and console ptys to be
created and both be used as input. There was an implementation gap
though with the queue_input_bytes needing to be modified so the pty
handlers for serial and console could access the methods on the serial
and console structures directly. Without this change only a single
input source could be processed as the console would switch based on
its input type (this is still valid for tty and isn't otherwise
modified).

Signed-off-by: William Douglas <william.r.douglas@gmail.com>
2021-02-09 10:03:28 +00:00
dependabot-preview[bot]
a59fbf0e37 build(deps): bump unicode-normalization from 0.1.16 to 0.1.17
Bumps [unicode-normalization](https://github.com/unicode-rs/unicode-normalization) from 0.1.16 to 0.1.17.
- [Release notes](https://github.com/unicode-rs/unicode-normalization/releases)
- [Commits](https://github.com/unicode-rs/unicode-normalization/compare/v0.1.16...v0.1.17)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-09 06:43:17 +00:00
Rob Bradford
a6b839b35c build: Update to latest kvm-ioctls
Update the version of the fork pointed to which has been rebased on the
latest upstream.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-08 18:16:18 +00:00
dependabot-preview[bot]
aa3d5cfbfe build(deps): bump libc from 0.2.85 to 0.2.86
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.85 to 0.2.86.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.85...0.2.86)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-08 16:19:06 +00:00
Wei Liu
ddd0552c83 virtio-devices: vhost-user-net: unpark control queue thread in resume
This thread is virtio-net specific, so it is not handled in the common
virtio device code.

The non-vhost implementation resumes the thread itself. Do the same
thing for vhost-user-net.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-08 16:16:13 +00:00
Wei Liu
1fc8c9165a vmm: drop two unused errors
Their last users were gone in af3c6c34c3.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-02-08 16:15:31 +00:00
dependabot-preview[bot]
47f47ad53f build(deps): bump iced-x86 from 1.10.0 to 1.10.3
Bumps [iced-x86](https://github.com/0xd4d/iced) from 1.10.0 to 1.10.3.
- [Release notes](https://github.com/0xd4d/iced/releases)
- [Commits](https://github.com/0xd4d/iced/compare/v1.10.0...v1.10.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-08 07:24:59 +00:00
dependabot-preview[bot]
69d1186ccb build(deps): bump serde_json from 1.0.61 to 1.0.62
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.61 to 1.0.62.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.61...v1.0.62)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-06 10:01:35 +00:00
dependabot-preview[bot]
d0601a1473 build(deps): bump idna from 0.2.0 to 0.2.1
Bumps [idna](https://github.com/servo/rust-url) from 0.2.0 to 0.2.1.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/idna-v0.2.0...v0.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-05 11:17:52 +00:00
Rob Bradford
b91b829a28 openapi: Update API spec to include hugepage_size parameter
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-05 09:24:02 +00:00
Rob Bradford
7928a697dc vmm: Support configurable huge pages in MemoryManager
Use the newly added hugepages_size option if provided by the user to
pick a huge page size when creating the memfd region. If none is
specified use the system default.

Sadly different huge pages cannot be tested by an integration test as
creating a pool of the non-default size cannot be done at runtime
(requires kernel to be booted with certain parameters.)

TETS=Manually tested with a kernel booted with both 1GiB and 2MiB huge
pages (hugepagesz=1G hugepages=1 hugepagesz=2M hugepages=512)

Fixes: #2230

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-05 09:24:02 +00:00
Rob Bradford
b5fb744828 docs: Update documentation for hugepage_size option
Including a warning that the user is respsonsible for ensuring that they
have sufficient pages of the specified size.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-05 09:24:02 +00:00
Rob Bradford
29607f38ad vmm: config: Add a hugepage_size option
This allows the user to use an alternative huge page size otherwise the
default size will be used.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-05 09:24:02 +00:00
Rob Bradford
9c3a73706f vmm: config: Remove ineffectual code from test
Remove a change the invalid configuration that is not ever tested again.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-02-05 09:24:02 +00:00
Muminul Islam
024675d1d8 scripts: error exit if aarch64 test is run on MSHV
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-02-05 09:22:58 +00:00
dependabot-preview[bot]
2c913b8130 build(deps): bump thread_local from 1.1.2 to 1.1.3
Bumps [thread_local](https://github.com/Amanieu/thread_local-rs) from 1.1.2 to 1.1.3.
- [Release notes](https://github.com/Amanieu/thread_local-rs/releases)
- [Commits](https://github.com/Amanieu/thread_local-rs/compare/v1.1.2...v1.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-04 20:53:44 +00:00
Sebastien Boeuf
c397c9c95e vmm, virtio-devices: mem: Don't use MADV_DONTNEED on hugepages
This commit introduces a new information to the VirtioMemZone structure
in order to know if the memory zone is backed by hugepages.

Based on this new information, the virtio-mem device is now able to
determine if madvise(MADV_DONTNEED) should be performed or not. The
madvise documentation specifies that MADV_DONTNEED advice will fail if
the memory range has been allocated with some hugepages.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
Signed-off-by: Hui Zhu <teawater@antfin.com>
2021-02-04 17:52:30 +00:00
Sebastien Boeuf
54f814f34a virtio-devices: mem: Refactor MemEpollHandler
This commit performs some refactoring to make all functions a method
from a specific object, and in particular methods for MemEpollHandler.

The point is to simplify the code to make it more readable.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-04 17:52:30 +00:00
Sebastien Boeuf
ad8adcb955 virtio-devices: mem: Adjust based on specification
Adjust the code to comply better with the virtio-mem specification by
adding some validation for the virtio-mem configuration, but also by
updating the virtio-mem configuration itself.

Nowhere in the virtio-mem specification is stated the usable region size
must be adjusted everytime the plugged size changes. For simplification
reason, and without going against the specification, the usable region
size is now kept static, setting its value to the size of the whole
region.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-04 17:52:30 +00:00
Sebastien Boeuf
f24094392e virtio-devices: mem: Improve semantic around Resize object
By introducing a ResizeSender object, we avoid having a Resize clone
with a different content than the original Resize object.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-04 17:52:30 +00:00
Samuel Ortiz
eee218f1f5 hypervisor: emulator: Add MOVZX unit tests
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2021-02-03 14:11:18 +01:00
Samuel Ortiz
5ada3f59c8 hypervisor: emulator: Emulate MOVZX
MOV R/RM is a special case of MOVZX, so we generalize the mov_r_rm macro
to make it support both instructions.

Fixes: #2227

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2021-02-03 14:11:18 +01:00
dependabot-preview[bot]
89008a49cf build(deps): bump libc from 0.2.84 to 0.2.85
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.84 to 0.2.85.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.84...0.2.85)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-02 07:47:41 +00:00
dependabot-preview[bot]
295f4df7bf build(deps): bump ssh2 from 0.9.0 to 0.9.1
Bumps [ssh2](https://github.com/alexcrichton/ssh2-rs) from 0.9.0 to 0.9.1.
- [Release notes](https://github.com/alexcrichton/ssh2-rs/releases)
- [Commits](https://github.com/alexcrichton/ssh2-rs/compare/0.9.0...0.9.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-01 18:48:26 +00:00
dependabot-preview[bot]
769194cac8 build(deps): bump libssh2-sys from 0.2.20 to 0.2.21
Bumps [libssh2-sys](https://github.com/alexcrichton/ssh2-rs) from 0.2.20 to 0.2.21.
- [Release notes](https://github.com/alexcrichton/ssh2-rs/releases)
- [Commits](https://github.com/alexcrichton/ssh2-rs/compare/libssh2-sys-0.2.20...libssh2-sys-0.2.21)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-01 18:13:04 +00:00
Sebastien Boeuf
13a3ac6a77 block_util: Add unit testing for VHD format
The vhd module is the implementation of the VHD specification, which is
why it is important to unit test it.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-01 13:45:08 +00:00
Sebastien Boeuf
b26777cb01 ci: Add integration test for VHD format
Let's create a fixed VHD disk file from the existing RAW file thanks to
qemu-img, and create a new integration test to validate that
Cloud-Hypervisor can boot VHD disk image.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-01 13:45:08 +00:00
Sebastien Boeuf
24c8cce012 block_util: Add synchronous support for fixed VHD disk files
Relying on the simplified version of the synchronous support for RAW
disk files, the new fixed_vhd_sync module in the block_util crate
introduces the synchronous support for fixed VHD disk files.

With this patch, the fixed VHD support is complete as it is implemented
in both synchronous and asynchronous versions.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-01 13:45:08 +00:00
Sebastien Boeuf
c6854c5a97 block_util: Simplify RAW synchronous implementation
Using directly preadv and pwritev, we can simply use a RawFd instead of
a file, and we don't need to use the more complex implementation from
the qcow crate.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-01 13:45:08 +00:00
Sebastien Boeuf
b2e5dbaecb block_util, vmm: Add fixed VHD asynchronous implementation
This commit adds the asynchronous support for fixed VHD disk files.

It introduces FixedVhd as a new ImageType, moving the image type
detection to the block_util crate (instead of qcow crate).

It creates a new vhd module in the block_util crate in order to handle
VHD footer, following the VHD specification.

It creates a new fixed_vhd_async module in the block_util crate to
implement the asynchronous version of fixed VHD disk file. It relies on
io_uring.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-02-01 13:45:08 +00:00
Muminul Islam
5bbf2dca80 vmm: Remove unneeded return statement
This unneeded return statement giving clippy warnings

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-01-30 08:58:09 +00:00
dependabot-preview[bot]
1df952726a build(deps): bump libc from 0.2.83 to 0.2.84
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.83 to 0.2.84.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-29 06:21:38 +00:00
Rob Bradford
9ea087597f vmm: acpi: Ensure field size address matches ResourceTag
dsdt.dsl    960:             CreateDWordField (MR64, \_SB.MHPC.MCRS._Y00._MIN, MINL)  // _MIN: Minimum Base Address
Warning  3128 -                              ResourceTag larger than Field ^  (Size mismatch, Tag: 64 bits, Field: 32 bits)

dsdt.dsl    962:             CreateDWordField (MR64, \_SB.MHPC.MCRS._Y00._MAX, MAXL)  // _MAX: Maximum Base Address
Warning  3128 -                              ResourceTag larger than Field ^  (Size mismatch, Tag: 64 bits, Field: 32 bits)

dsdt.dsl    964:             CreateDWordField (MR64, \_SB.MHPC.MCRS._Y00._LEN, LENL)  // _LEN: Length
Warning  3128 -                              ResourceTag larger than Field ^  (Size mismatch, Tag: 64 bits, Field: 32 bits)

Fixes: #2216

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-28 14:30:34 +01:00
Rob Bradford
952f9bd3fe vmm: acpi: Remove incorrect return statement
_EJx built in should not return.

dsdt.dsl    813:                 Return (CEJ0 (0x00))
Warning  3104 -                            ^ Reserved method should not return a value (_EJ0)

dsdt.dsl    813:                 Return (CEJ0 (0x00))
Error    6080 -                            ^ Called method returns no value

Fixes: #2216

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-28 14:30:34 +01:00
Rob Bradford
c29caf2a85 vmm: acpi: Fix incorrect mutex timeout value
The mutex timeout should be 0xffff rather than 0xfff to disable the
timeout feature.

dsdt.dsl    745:             Acquire (\_SB.PRES.CPLK, 0x0FFF)
Warning  3130 -                                           ^ Result is not used, possible operator timeout will be missed

dsdt.dsl    767:             Acquire (\_SB.PRES.CPLK, 0x0FFF)
Warning  3130 -                                           ^ Result is not used, possible operator timeout will be missed

dsdt.dsl    775:             Acquire (\_SB.PRES.CPLK, 0x0FFF)
Warning  3130 -                                           ^ Result is not used, possible operator timeout will be missed

Fixes: #2216

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-28 14:30:34 +01:00
Bo Chen
5fbeacabad tests: Remove manual delete of the TAP interface in test_tap_from_fd
By using `net_util::open_tap` to create the TAP interface, the created
interface will be deleted when the returned variable (`net_utils::Tap`)
is dropped.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-01-28 09:11:39 +00:00
Bo Chen
f7502057d9 tests: Extend test_tap_from_fd() with using multiple TAP fds
Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-01-28 09:11:39 +00:00
Bo Chen
ff54909d2c vmm: openapi: Update the fd field of the NetConfig definition
Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-01-28 09:11:39 +00:00
Bo Chen
6664e5a6e7 net_util, virtio-devices, vmm: Accept multiple TAP fds
This patch enables multi-queue support for creating virtio-net devices by
accepting multiple TAP fds, e.g. '--net fds=3:7'.

Fixes: #2164

Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-01-28 09:11:39 +00:00
Rob Bradford
5209026f52 tests: Port test_tap_from_fd to use net_util::open_tap
This removes the need for manually creating the interface and assigning
the IP.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-28 09:11:39 +00:00
Rob Bradford
5db9b0ec99 net_util: Support supplying flags to open_tap() helper
This helper can open a TAP device and configure the interface on it. If
the device needs to be opened multiple times for MQ then it also handles
that correctly.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-28 09:11:39 +00:00
Muminul Islam
c4fb0f36bd scripts: Use hypervisor variable instead of kvm
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-01-28 09:11:02 +00:00
Muminul Islam
9cdcbb5121 script: Modify unit tests to support mshv
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-01-28 09:11:02 +00:00
Muminul Islam
a194dad98c arch, vmm: Run KVM specific unit tests with kvm feature guard
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-01-28 09:11:02 +00:00
Muminul Islam
b339aa6b88 scripts: Export devices(/dev/kvm or /dev/mshv) based on hypervisor
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2021-01-28 09:11:02 +00:00
Sebastien Boeuf
276c9a10fd docs: Clarify VFIO limitations regarding NVIDIA cards
Because of the behavior of the NVIDIA proprietary driver, we can't
expect NVIDIA cards with only MSI support to be functioning correctly
after they've been passed through with Cloud-Hypervisor.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-28 09:10:45 +00:00
dependabot-preview[bot]
36360c7630 build(deps): bump libc from 0.2.82 to 0.2.83
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.82 to 0.2.83.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.82...0.2.83)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-28 06:30:03 +00:00
Wei Liu
b959849149 vmm: drop unnecessary semicolon
Building with 1.51 nightly produces the following warning:

warning: unnecessary trailing semicolon
   --> vmm/src/device_manager.rs:396:6
    |
396 |     };
    |      ^ help: remove this semicolon
    |
    = note: `#[warn(redundant_semicolons)]` on by default

warning: 1 warning emitted

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-01-27 14:43:20 +00:00
dependabot-preview[bot]
50a89c6808 build(deps): bump rand from 0.8.2 to 0.8.3
Bumps [rand](https://github.com/rust-random/rand) from 0.8.2 to 0.8.3.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-27 13:26:04 +00:00
dependabot-preview[bot]
192d69d601 build(deps): bump log from 0.4.13 to 0.4.14
Bumps [log](https://github.com/rust-lang/log) from 0.4.13 to 0.4.14.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.13...0.4.14)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-27 12:48:52 +00:00
Rob Bradford
b8342ebeb4 scripts: Don't download container on help invocations
Delay downloading the container until it is being used.

Fixes: #2065

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-26 13:29:05 +01:00
Rob Bradford
58009af85d tests: Add clippy check for integration test building
Ensure that we try and keep the integration tests clippy clean.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-26 13:23:16 +01:00
dependabot-preview[bot]
0655f25da4 build(deps): bump serde from 1.0.121 to 1.0.123
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.121 to 1.0.123.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.121...v1.0.123)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-26 10:49:12 +00:00
dependabot-preview[bot]
2547f144cd build(deps): bump tinyvec from 1.1.0 to 1.1.1
Bumps [tinyvec](https://github.com/Lokathor/tinyvec) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/Lokathor/tinyvec/releases)
- [Changelog](https://github.com/Lokathor/tinyvec/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Lokathor/tinyvec/compare/v1.1.0...v1.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-26 09:43:10 +00:00
dependabot-preview[bot]
ec7700544c build(deps): bump serde_derive from 1.0.122 to 1.0.123
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.122 to 1.0.123.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.122...v1.0.123)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-26 09:42:42 +00:00
Rob Bradford
0c60fa8268 net_util: tests: Avoid trying to create TAP devices simultaneously
The unit tests ask the Linux kernel to generate a TAP device name on
demand by passing in a format string. I suspect, but haven't been able
to confirm that there might be a rare race that triggers when creating
lots of devices in a short period of time. This is appearing in our unit
test as the occassional flake of the test_tap_read() which although it
has successfully created the device it fails to set the IP address on it
when looking it back up by it's name.

Since this is the most frequent cause of failures on our CI use a lock
to ensure that multiple TAP devices are not created simultaneously.

Fixes: #2135

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-26 10:42:02 +01:00
Rob Bradford
76e15a4240 vmm: acpi: Support compiling ACPI code on aarch64
This skeleton commit brings in the support for compiling aarch64 with
the "acpi" feature ready to the ACPI enabling. It builds on the work to
move the ACPI hotplug devices from I/O ports to MMIO and conditionalises
any code that is x86_64 only (i.e. because it uses an I/O port.)

Filling in the aarch64 specific details in tables such as the MADT it
out of the scope.

See: #2178

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-26 15:19:02 +08:00
Vineeth Pillai
7659055eec mshv: Use in-kernel ioeventfd mechanism
Signed-off-by: Vineeth Pillai <viremana@linux.microsoft.com>
2021-01-25 21:20:29 +00:00
Vineeth Pillai
5209bd6120 mshv: Use in-kernel irqfd mechanism
Convert MshvIrqRoutingEntry functions to methods as well.

Signed-off-by: Vineeth Pillai <viremana@linux.microsoft.com>
2021-01-25 21:20:29 +00:00
Rob Bradford
c2acea5156 tests: Support filtering integration tests
e.g.

scripts/dev_cli.sh tests --integration -- --test-filter test_watchdog

This used to be supported by passing "$@" but was broken when multiple
hypervisor support was added.

Fixes: #2182

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-25 17:34:10 +00:00
Wei Liu
fd9972ecc5 hypervisor: fix exception vector numbers
According to Intel SDM, #DE should be 0 and #DB should be 1.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-01-25 16:39:08 +00:00
dependabot-preview[bot]
ca4ab99f42 build(deps): bump serde_derive from 1.0.121 to 1.0.122
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.121 to 1.0.122.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.121...v1.0.122)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-25 12:16:07 +00:00
dependabot-preview[bot]
977d824cf8 build(deps): bump syn from 1.0.59 to 1.0.60
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.59 to 1.0.60.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.59...1.0.60)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-25 09:23:17 +00:00
dependabot-preview[bot]
38fa630ce7 build(deps): bump thread_local from 1.1.1 to 1.1.2
Bumps [thread_local](https://github.com/Amanieu/thread_local-rs) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/Amanieu/thread_local-rs/releases)
- [Commits](https://github.com/Amanieu/thread_local-rs/compare/v1.1.1...v1.1.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-25 09:23:07 +00:00
Rob Bradford
a59b361058 resources: Use Rust 1.49.0 in container
This is the latest stable version.

Fixes: #2092

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-24 17:05:07 +00:00
dependabot-preview[bot]
fa5b5fd602 build(deps): bump serde from 1.0.120 to 1.0.121
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.120 to 1.0.121.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.120...v1.0.121)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-24 16:21:11 +00:00
dependabot-preview[bot]
7765398045 build(deps): bump serde_derive from 1.0.120 to 1.0.121
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.120 to 1.0.121.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.120...v1.0.121)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-24 15:37:25 +00:00
dependabot-preview[bot]
9282ad82b0 build(deps): bump syn from 1.0.58 to 1.0.59
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.58 to 1.0.59.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.58...1.0.59)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-23 11:58:14 +00:00
dependabot-preview[bot]
a09feecc36 build(deps): bump thread_local from 1.1.0 to 1.1.1
Bumps [thread_local](https://github.com/Amanieu/thread_local-rs) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/Amanieu/thread_local-rs/releases)
- [Commits](https://github.com/Amanieu/thread_local-rs/compare/v1.1.0...v1.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-23 11:57:46 +00:00
Sebastien Boeuf
060df69f07 block_util: Factorize common code for synchronous implementations
Since QCOW and RAW synchronous implementation are very close, it makes
sense to introduce some common functions that can be shared between
these two.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-22 16:10:34 +00:00
Sebastien Boeuf
f6c8e4b045 vmm: device_manager: Add info!() message about disk file backend
It might be useful debugging information for the user to know what kind
of disk file implementation is in use.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-22 16:10:34 +00:00
Sebastien Boeuf
2824642e80 virtio-devices: Rename BlockIoUring to Block
Now that BlockIoUring is the only implementation of virtio-block,
handling both synchronous and asynchronous backends based on the
AsyncIo trait, we can rename it to Block.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-22 16:10:34 +00:00
Sebastien Boeuf
41cfdb50cd virtio-devices: Remove virtio-block synchronous implementation
Now that both synchronous and asynchronous backends rely on the
asynchronous version of virtio-block (namely BlockIoUring), we can
get rid of the synchronous version (namely Block).

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-22 16:10:34 +00:00
Sebastien Boeuf
12e20effd7 block_util: Port synchronous QCOW file to AsyncIo trait
Based on the synchronous QCOW file implementation present in the qcow
crate, we created a new qcow_sync module in block_util that ports this
synchronous implementation to the AsyncIo trait.

The point is to reuse virtio-blk asynchronous implementation for both
synchronous and asynchronous backends.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-22 16:10:34 +00:00
Sebastien Boeuf
9fc86a91e2 block_util: Port synchronous RAW file to AsyncIo trait
Based on the synchronous RAW file implementation present in the qcow
crate, we created a new raw_sync module in block_util that ports this
synchronous implementation to the AsyncIo trait.

The point is to reuse virtio-blk asynchronous implementation for both
synchronous and asynchronous backends.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-22 16:10:34 +00:00
Sebastien Boeuf
da8ce25abf virtio-devices: Use asynchronous traits for virtio-blk io_uring
Based on the new DiskFile and AsyncIo traits, the implementation of
asynchronous block support does not have to be tied to io_uring anymore.
Instead, the only thing the virtio-blk implementation knows is that it
is using an asynchronous implementation of the underlying disk file.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-22 16:10:34 +00:00
Sebastien Boeuf
23e3b022eb block_util: Implement asynchronous traits for RAW disk file
This provides the implementation of DiskFile and AsyncIo for the RAW
file format.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-22 16:10:34 +00:00
Sebastien Boeuf
30033bdaea block_util: Add new traits for handling disk files asynchronously
Both DiskFile and AsyncIo traits are introduced to allow all kind of
files (RAW, QCOW, VHD) to be able to handle asynchronous access to the
underlying file.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-22 16:10:34 +00:00
Rob Bradford
d578c408b7 vmm: acpi: Move DeviceManager ACPI device to an MMIO address
Migrate the DeviceManager from a fixed I/O port address to an allocated
MMIO address.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-22 16:08:41 +01:00
Rob Bradford
55a3a38e14 vmm: acpi: Move CpuManager ACPI device to an MMIO address
Migrate the CpuManager from a fixed I/O port address to an allocated
MMIO address.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-22 16:08:41 +01:00
Rob Bradford
6006068951 vmm: acpi: Move MemoryManager ACPI device to an MMIO address
Migrate the MemoryManager from a fixed I/O port address to an allocated
MMIO address.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-22 16:08:41 +01:00
Rob Bradford
28ab6cea0e vmm: acpi: Move ACPI GED device to MMIO bus
Currently the GED control is in a fixed I/O port address but instead use
an MMIO address that has been chosen by the allocator.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-22 16:08:41 +01:00
Rob Bradford
4ebeeb1310 vmm: device_manager: Rename shutdown ACPI device
Rename the variable for the shutdown device to clarify it's purpose in
the function.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-22 16:08:41 +01:00
dependabot-preview[bot]
77f6cd5ef4 build(deps): bump mshv-bindings from 2ded6da to 3a7d7ba
Bumps [mshv-bindings](https://github.com/cloud-hypervisor/mshv) from `2ded6da` to `3a7d7ba`.
- [Release notes](https://github.com/cloud-hypervisor/mshv/releases)
- [Commits](2ded6daf97...3a7d7ba07e)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-22 07:09:54 +00:00
dependabot-preview[bot]
f2c2a44ca6 build(deps): bump backtrace from 0.3.55 to 0.3.56
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.55 to 0.3.56.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.55...0.3.56)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-21 17:18:29 +00:00
Bo Chen
8d418e1fca vmm: Refine the seccomp filter list for the vCPU thread
This patch refines the sccomp filter list for the vCPU thread, as we are
no longer spawning virtio-device threads from the vCPU thread.

Fixes: #2170

Signed-off-by: Bo Chen <chen.bo@intel.com>
2021-01-21 09:41:40 +00:00
Rob Bradford
c90f77e399 virtio-devices: Enforce a minimum number of queues
Even though the driver can provide fewer queues than those advertised
for some device types their is a minimum number that is required for
operation.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-20 18:54:36 +01:00
Rob Bradford
a105089702 virtio-devices: Support driver programming fewer queues
It is permissable for the driver to program fewer queues than offered by
the device. Filter the queues so that only the ready ones are included
and check that they have valid addresses configured.

Fixes: #2136

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-20 18:54:36 +01:00
Rob Bradford
c366efc19e virtio-devices: block, block_io_uring: Don't assume max queue count
Don't assume that the number of queues provided match the number of
queues offered. The virtio spec allows the driver to program fewer
queues.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-20 18:54:36 +01:00
Rob Bradford
c9b68a3be6 .github: Simplify cross-build action
Remove unnecessary commands to simplify the cross-build action.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-20 18:51:42 +01:00
Xie Yongji
72b736153e vm-virtio: Fix update_avail_event() memory ordering
We should use full memory barrier to ensure both guest and us
can see the correct avail_idx and avail_event_idx. Something
like this pattern:

VM:                           CLH:
update vring.avail->idx       update avail_event_idx
mb()                          mb()
read avail_event_idx          read vring.avail->idx

Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
2021-01-20 08:32:56 +01:00
dependabot-preview[bot]
79a2afe44e build(deps): bump serde from 1.0.119 to 1.0.120
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.119 to 1.0.120.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.119...v1.0.120)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-19 08:19:23 +00:00
dependabot-preview[bot]
7d299f1055 build(deps): bump serde_derive from 1.0.119 to 1.0.120
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.119 to 1.0.120.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.119...v1.0.120)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-19 07:37:46 +00:00
Rob Bradford
23f9ec50fb virtio-devices: Simplify virtio device reset
Rather than having to give and return the ioeventfd used for a device
clone them each time. This will make it simpler when we start handling
the driver enabling fewer queues than advertised by the device.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-18 15:05:54 +00:00
dependabot-preview[bot]
c99d3f70ac build(deps): bump signal-hook from 0.3.3 to 0.3.4
Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.3.3 to 0.3.4.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/commits/v0.3.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-17 20:27:52 +00:00
dependabot-preview[bot]
33d339c406 build(deps): bump hermit-abi from 0.1.17 to 0.1.18
Bumps [hermit-abi](https://github.com/hermitcore/libhermit-rs) from 0.1.17 to 0.1.18.
- [Release notes](https://github.com/hermitcore/libhermit-rs/releases)
- [Commits](https://github.com/hermitcore/libhermit-rs/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-17 20:27:34 +00:00
Rob Bradford
22649c4a87 virtio-devices: Upon reset reap/join the device threads
We have killed the device thread by writing to the exit EventFd but we
should wait for them to quit to ensure consistency.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-14 17:25:14 +01:00
dependabot-preview[bot]
ef363d79fb build(deps): bump humantime from 2.0.1 to 2.1.0
Bumps [humantime](https://github.com/tailhook/humantime) from 2.0.1 to 2.1.0.
- [Release notes](https://github.com/tailhook/humantime/releases)
- [Commits](https://github.com/tailhook/humantime/compare/v2.0.1...v2.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-13 21:49:27 +00:00
Rob Bradford
92f3f86669 tests: Add integration test for power button
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
f6d6495b93 resources: Add CONFIG_ACPI_BUTTON to linux-config-x86_64
Thise is needed to enable clean shutdown of the guest via an ACPI power
button.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
68d90cbe65 opeenapi: Add power button API entry point
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
d0b59ca3a0 ch-remote: Add support for "power-button"
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
981bb72a09 vmm: api: Add "power-button" API entry point
This will lead to the triggering of an ACPI button inside the guest in
order to cleanly shutdown the guest.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
843a010163 vmm: vm, device_manager: Trigger a power button notification via ACPI
Use the ACPI GED device to trigger a notitifcation of type
POWER_BUTTON_CHANGED which will ultimately lead to the guest being
notified.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
c381b36b87 devices: acpi: Trigger the power button upon GED event
If a GED event of type 0x8 (for power button) is received notify the
guest OS that this button device has been activated.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
ff4d378f1b vmm: device_manager: acpi: Add a power button (PNP0C0C)
Add a power button to the devices defined in the \_SB

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
74a4aba257 devices: Add notification value for power button
Add a value to the ACPI notification type to indicate the power button
is to be triggered.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
7b376fa8e0 devices: acpi: Generalise the HotPlugNotificationFlags
Renamed this bitfield as it will also be used for non-hotplug purposes
such as synthesising a power button.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 17:00:39 +00:00
Rob Bradford
23afe89089 virtio-devices: Derive thread names from device ids
In order to make the thread naming more useful derive their name from
the device id (which can be supplied by the user) and a device specific
suffix that has details of the individual queue (or queue pair.)

e.g.

rob@artemis:~$ pstree -p -c -l -t `pidof cloud-hypervisor`
cloud-hyperviso(27501)─┬─{_console}(27525)
                       ├─{_disk0_q0}(27529)
                       ├─{_disk0_q1}(27532)
                       ├─{_net1_ctrl}(27533)
                       ├─{_net1_qp0}(27534)
                       ├─{_net1_qp1}(27535)
                       ├─{_rng}(27526)
                       ├─{http-server}(27504)
                       ├─{seccomp_signal_}(27502)
                       ├─{signal_handler}(27523)
                       ├─{vcpu0}(27520)
                       ├─{vcpu1}(27522)
                       └─{vmm}(27503)

Fixes: #2077

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-13 16:56:44 +01:00
Sebastien Boeuf
3b43551d98 ci: Use device mapper to avoid copying Windows image
The Windows image is quite large (about 20GiB), hence it takes some time
to copy it for every test in order to avoid potential corruption.

One way to mitigate that without compromising on safety between each
test is by using device mapper. By creating a read-only base, we ensure
the image won't be modified by any of the tests, and by creating one
snapshot for each test, we avoid copying the entire image each time.
A dedicated Copy On Write disk image is created to handle any change
that might be performed on the base image, letting the tests behave as
expected.

Fixes #2155

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-13 10:48:06 +00:00
Sebastien Boeuf
c3df42d42f ci: Copy Windows image for each integration test
By relying on the Guest object, Windows dedicated tests copy the Windows
guest image before booting from it. The point being to avoid corruption
between multiple tests. This is already how the rest of the integration
tests work, Windows tests were the only ones missing this feature.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-13 10:48:06 +00:00
dependabot-preview[bot]
6b424b5f6b build(deps): bump rand from 0.8.1 to 0.8.2
Bumps [rand](https://github.com/rust-random/rand) from 0.8.1 to 0.8.2.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/0.8.1...0.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-13 10:41:29 +00:00
dependabot-preview[bot]
b2dd03b2dc build(deps): bump getrandom from 0.1.15 to 0.1.16
Bumps [getrandom](https://github.com/rust-random/getrandom) from 0.1.15 to 0.1.16.
- [Release notes](https://github.com/rust-random/getrandom/releases)
- [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/getrandom/compare/v0.1.15...v0.1.16)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-13 07:19:44 +00:00
dependabot-preview[bot]
4ebca7f4ea build(deps): bump thread_local from 1.0.1 to 1.1.0
Bumps [thread_local](https://github.com/Amanieu/thread_local-rs) from 1.0.1 to 1.1.0.
- [Release notes](https://github.com/Amanieu/thread_local-rs/releases)
- [Commits](https://github.com/Amanieu/thread_local-rs/compare/v1.0.1...v1.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-13 07:19:20 +00:00
Rob Bradford
d036210ecd build: deps: Bump serde and serde_derive together
The tight coupling of these packages caused issues for dependabot trying
to update them separately so instead combine them together into one
update.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-12 11:16:22 +01:00
Rob Bradford
39d080e0c1 main: Give a friendly message when we get a seccomp violation
If we receive SIGSYS and identify it as a seccomp violation then give
friendly instructions on how to debug further. We are unable to decode
the siginfo_t struct ourselves due to https://github.com/rust-lang/libc/issues/716

Fixes: #2139

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-12 09:23:29 +00:00
dependabot-preview[bot]
d83c9a74f4 build(deps): bump tempfile from 3.1.0 to 3.2.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/Stebalien/tempfile/releases)
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/NEWS)
- [Commits](https://github.com/Stebalien/tempfile/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-12 08:21:47 +00:00
dependabot-preview[bot]
d26866e018 build(deps): bump libc from 0.2.81 to 0.2.82
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.81 to 0.2.82.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.81...0.2.82)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-12 07:06:21 +00:00
Rob Bradford
ba7864e111 tests: Send systemd journal to console
This will aid debugging of test issues especially those for critical
services during the boot.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-11 16:38:07 +00:00
Rob Bradford
5881809126 tests: Update integration tests to use new image without pollinate
This image does not have the pollinate service which can sometimes fail
and prevent SSH from starting as it marks itself as a prerequisite. This
service will never fully succeed as it tries to make a network
connection which will fail inside our test VMs.

Fixes: #2113

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-11 16:38:07 +00:00
Rob Bradford
21ba34542d doc: Update documentation to note removal of pollinate package
Also update to include a simple version number in the image name.

See: #2113

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-11 16:38:07 +00:00
dependabot-preview[bot]
ab456affb3 build(deps): bump log from 0.4.11 to 0.4.13
Bumps [log](https://github.com/rust-lang/log) from 0.4.11 to 0.4.13.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.11...0.4.13)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-11 12:49:34 +00:00
Rob Bradford
852bf0b2ce scripts, resources: aarch64: Update to Linux kernel v5.10 base
This allows the removal of most of the custom patches in particular all
the virtio-fs ones have been integrated.

Fixes: #2070

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-11 12:56:05 +01:00
Rob Bradford
642a1a610f scripts: Update to Linux fork using latest virtio-iommu patches
The ch-5.10.6 branch is rebased on top of Linux stable and includes the
latest virtio-iommu support patches.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-11 12:56:05 +01:00
Rob Bradford
bdfe6a69ef virtio-devices: iommu: Update to latest version of IOMMU spec
This is order to support the latest patches from
https://jpbrucker.net/git/linux/commit/?h=virtio-iommu/devel&id=ddc3e6ce3f534af827a9c8f91e12a7082de8ec61

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-11 12:56:05 +01:00
Sebastien Boeuf
7889fc9207 scripts: Run test container with the default bridge network
Using --net=host is not necessary for any of the integration tests, so
let's use the default network option called "bridge".

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-11 11:18:48 +00:00
Rob Bradford
cb826aa2f1 vmm: seccomp: Add open() to vCPU permitted syscalls
Older libc (like RHEL7) uses open() rather than openat(). This was
demonstrated through a failure to open /etc/localtime as used by
gmtime() libc call trigged from the vCPU thread (CMOS device.)

Fixes: #2111

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-11 11:24:13 +01:00
Sebastien Boeuf
df522cf12c ci: Re-enable test_windows_guest_snapshot_restore
This reverts commit 72d054bf40.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-11 09:32:58 +00:00
Rob Bradford
a920ef48de Revert "tests: Temporarily disable test_watchdog on aarch64"
This reverts commit 49b49421d0.

Probable solution was applied in f70852c04b

Fixes: #2103

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-11 10:26:52 +01:00
dependabot-preview[bot]
9cd8aa1af9 build(deps): bump addr2line from 0.14.0 to 0.14.1
Bumps [addr2line](https://github.com/gimli-rs/addr2line) from 0.14.0 to 0.14.1.
- [Release notes](https://github.com/gimli-rs/addr2line/releases)
- [Changelog](https://github.com/gimli-rs/addr2line/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gimli-rs/addr2line/compare/0.14.0...0.14.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-11 08:09:26 +00:00
dependabot-preview[bot]
f6b63af06d build(deps): bump anyhow from 1.0.37 to 1.0.38
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.37 to 1.0.38.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.37...1.0.38)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-11 08:08:59 +00:00
dependabot-preview[bot]
8ff1142bf4 build(deps): bump signal-hook from 0.3.2 to 0.3.3
Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.3.2 to 0.3.3.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-11 08:08:39 +00:00
dependabot-preview[bot]
5de4d03238 build(deps): bump regex from 1.4.2 to 1.4.3
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.2 to 1.4.3.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.4.2...1.4.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-08 18:20:40 +00:00
dependabot-preview[bot]
302e4c17d2 build(deps): bump smallvec from 1.6.0 to 1.6.1
Bumps [smallvec](https://github.com/servo/rust-smallvec) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/compare/v1.6.0...v1.6.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-08 18:17:57 +00:00
Sebastien Boeuf
b49c7dfd65 ci: Always download the Windows image
Because we're back on transient builder, let's download the image
everytime.

This reverts commit b5653d5278.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-08 17:18:28 +00:00
dependabot-preview[bot]
985da4d3fe build(deps): bump regex-syntax from 0.6.21 to 0.6.22
Bumps [regex-syntax](https://github.com/rust-lang/regex) from 0.6.21 to 0.6.22.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-08 16:45:25 +00:00
dependabot-preview[bot]
2ec7d1ae05 build(deps): bump linux-loader from acf9c21 to 2855be1
Bumps [linux-loader](https://github.com/rust-vmm/linux-loader) from `acf9c21` to `2855be1`.
- [Release notes](https://github.com/rust-vmm/linux-loader/releases)
- [Commits](acf9c21591...2855be15a7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-08 16:29:48 +00:00
dependabot-preview[bot]
fc47dd6645 build(deps): bump thiserror from 1.0.22 to 1.0.23
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.22 to 1.0.23.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.22...1.0.23)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-08 16:01:10 +00:00
dependabot-preview[bot]
2e924f458e build(deps): bump syn from 1.0.57 to 1.0.58
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.57 to 1.0.58.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.57...1.0.58)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-08 15:33:20 +00:00
Sebastien Boeuf
20fde6b924 tests: Increase sleep time in Windows integration tests
Some sporadic failures were due to an early connection to the VM while
it was not fully ready. Increasing sleep times fixes these issues.

Fixes #2104

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-08 15:03:04 +00:00
Rob Bradford
72d054bf40 tests: Teporarily disable test_windows_guest_snapshot_restore
This test is reliably failing on our CI and we need time to investigate
why.

See: #2126

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-08 15:03:04 +00:00
Rob Bradford
e0d79196c8 virtio-devices, vmm: Enhance debugging around virtio device activation
Sometimes when running under the CI tests fail due to a barrier not
being released and the guest blocks on an MMIO write. Add further
debugging to try and identify the issue.

See: #2118

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-08 14:06:44 +00:00
Bo Chen
a286e960ed tests: Remove the check on the "booted" message in 'wait_vm_boot()'
Given we already check the connected IP address matches the expected
guest IP address, the check on the "booted" message is not needed.

Fixes: #2117

Signed-off-by: Bo Chen <chen.bo@intel.com>
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-08 12:54:35 +00:00
Sebastien Boeuf
f70852c04b virtio-devices: Update seccomp filters for virtio-net thread
On aarch64, the openat() syscall was missing from the seccomp filters
list, preventing the test_watchdog from running properly.

Fixes #2103

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2021-01-08 12:37:32 +00:00
Rob Bradford
8f740f5c02 Jenkinsfile: Disable fast failing
Temporarily disable fast failing to try and make progress on CI
stabilisation to allow more test coverage.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-08 12:24:33 +00:00
Rob Bradford
17c9e2cd4b tests: Make boot notifications retry
If the guest fails to communicate with the test harness then retry.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-08 12:24:33 +00:00
Rob Bradford
b5653d5278 Jenkinsfile: Don't download the Windows images
They are already provisioned on the builder machine.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-07 17:31:02 +00:00
Rob Bradford
28abe63f46 tests: Fix test_virtio_block_disable_io_uring()
This test wasn't actually disabling io_uring.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-06 18:26:02 +00:00
Rob Bradford
49b49421d0 tests: Temporarily disable test_watchdog on aarch64
This test is very flaky and regularly causing CI failures. Until we can
identify the root cause we should disable this test.

See: #2103

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-06 18:26:02 +00:00
Rob Bradford
ef12216c19 tests: Add explicit QCOW2 based image test
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-06 18:26:02 +00:00
Rob Bradford
e05ed9240a tests: Use raw images for all tests
Simplify our image handling by not copying both QCOW2 and raw images for
every test. Allow the test to choose QCOW2 or raw by specifying the
image name manually. A follow on patch will add explicity QCOW2 tests.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-06 18:26:02 +00:00
Rob Bradford
c91d25ea64 tests: Don't swallow SSH errors
When an SSH command fails we want to be able to see, via a panic() why
and where it failed. Replace use of .unwrap_or_default() from SSH
command calls to ensure that we can see the location of the panic.

Also enhance the existing SSH output code to show the error if there is
one.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-06 13:51:26 +01:00
Rob Bradford
315a730128 virtio-devices: net: Reduce debug level of EVENT_IDX messages
This logging is too spammy for info!() level and should be handled as
debug!() level

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-06 13:51:26 +01:00
Rob Bradford
230e8a23a0 vm-virtio: queue: Reduce logging level of EVENT_IDX logs
This logging is too spammy for info!() level and should be handled as
debug!() level

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-06 13:51:26 +01:00
Mikko Ylinen
f583aa9d30 sgx: update virt EPC device path and docs
Based on the LKML feedback, the devices under /dev/sgx/* are
not justified. SGX RFC v40 moves the SGX device nodes to /dev/sgx_*
and this is reflected in kvm-sgx (next branch) too.

Update cloud-hypervisor code and documentation to follow this.

Signed-off-by: Mikko Ylinen <mikko.ylinen@intel.com>
2021-01-05 15:48:17 +00:00
Rob Bradford
76adea46d0 Revert "build(deps): bump linux-loader from acf9c21 to b18d387"
This reverts commit 7f1f335950.

This breaks the compilation on Fedora/RedHat machines by requiring
dpkg-deb in the build.rs

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-05 15:43:40 +00:00
dependabot-preview[bot]
7f1f335950 build(deps): bump linux-loader from acf9c21 to b18d387
Bumps [linux-loader](https://github.com/rust-vmm/linux-loader) from `acf9c21` to `b18d387`.
- [Release notes](https://github.com/rust-vmm/linux-loader/releases)
- [Commits](acf9c21591...b18d387889)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-05 11:15:02 +00:00
dependabot-preview[bot]
2e6f93e1a9 build(deps): bump smallvec from 1.5.1 to 1.6.0
Bumps [smallvec](https://github.com/servo/rust-smallvec) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/compare/v1.5.1...v1.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-05 07:02:07 +00:00
Rob Bradford
539708e45e scripts, resources: x86_64: Update to Linux kernel v5.10 base
This allows the removal of most of the custom patches in particular all
the virtio-fs ones have been integrated.

Partially fixes: #2070

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 22:44:40 +00:00
Rob Bradford
2e87d5ae02 tests: integration: Check different virtio-fs SHM region names
Depending on the kernel version the SHM region has a different name.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 22:44:40 +00:00
Rob Bradford
14af74cb5b vmm: seccomp: Allow clock_gettime() on the vCPU thread
If the vCPU thread calls log!() the time difference between the call
time and the boot up time is reported. On most environments and
architectures this covered by a vDSO call rather than a syscall. However
on some platforms this turns into a syscall.

Fixes: #2080

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 10:13:42 -08:00
dependabot-preview[bot]
73612401a5 build(deps): bump signal-hook from 0.2.2 to 0.3.2
Manually changed to reflect changes in mutability of signals and
rearrangement of constants.

Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.2.2 to 0.3.2.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/compare/v0.2.2...v0.3.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 17:59:41 +00:00
dependabot-preview[bot]
858f64b3dc build(deps): bump rand from 0.8.0 to 0.8.1
Bumps [rand](https://github.com/rust-random/rand) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/0.8.0...0.8.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-04 16:36:19 +00:00
dependabot-preview[bot]
225bedcb20 build(deps): bump arc-swap from 1.1.0 to 1.2.0
Bumps [arc-swap](https://github.com/vorner/arc-swap) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/vorner/arc-swap/releases)
- [Changelog](https://github.com/vorner/arc-swap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/arc-swap/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-04 13:32:54 +00:00
dependabot-preview[bot]
8367784cf3 build(deps): bump syn from 1.0.56 to 1.0.57
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.56 to 1.0.57.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.56...1.0.57)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-04 13:32:17 +00:00
Rob Bradford
ecc1da95dd arch: aarch64: Remove unnecessary literal cast
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
184baff355 hypervisor: kvm: aarch64: Use struct initialisation
error: field assignment outside of initializer for an instance created with Default::default()
Error:     --> hypervisor/src/kvm/mod.rs:1239:9
     |
1239 |         state.mp_state = self.get_mp_state()?;
     |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
note: consider initializing the variable with `kvm::aarch64::VcpuKvmState { mp_state: self.get_mp_state()?, ..Default::default() }` and removing relevant reassignments
    --> hypervisor/src/kvm/mod.rs:1237:9
     |
1237 |         let mut state = CpuState::default();
     |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
3ad94ddf7c devices: rtc_pl031: Don't use manual range check
error: manual `Range::contains` implementation
Error:    --> devices/src/legacy/rtc_pl031.rs:342:12
    |
342 |         if offset < AMBA_ID_HIGH && offset >= AMBA_ID_LOW {
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `(AMBA_ID_LOW..AMBA_ID_HIGH).contains(&offset)`
    |
    = note: `-D clippy::manual-range-contains` implied by `-D warnings`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
fabd63072b misc: Remove unnecessary literal casts
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
05cdef17f4 virtio-devices: mem, balloon: Use struct initialisation
error: field assignment outside of initializer for an instance created with Default::default()
   --> virtio-devices/src/mem.rs:496:9
    |
496 |         resp.resp_type = resp_type;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
note: consider initializing the variable with `mem::VirtioMemResp { resp_type: resp_type, ..Default::default() }` and removing relevant reassignments
   --> virtio-devices/src/mem.rs:495:9
    |
495 |         let mut resp = VirtioMemResp::default();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
6ccd32c904 pci: Remove manual range checks
error: manual `Range::contains` implementation
   --> pci/src/vfio.rs:948:12
    |
948 |         if (reg_idx >= PCI_CONFIG_BAR0_INDEX && reg_idx < PCI_CONFIG_BAR0_INDEX + BAR_NUMS)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `(PCI_CONFIG_BAR0_INDEX..PCI_CONFIG_BAR0_INDEX + BAR_NUMS).contains(&reg_idx)`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
50dae04c05 arch: smbios: Use literal types rather than casts
We must explicitly mark these values as u8 as the function that consumes
them takes a T and needs to use the specific width.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
44ac11efc7 arch: smbios: Use struct initialiser where possible
Checking arch v0.1.0 (/home/rob/src/cloud-hypervisor/arch)
error: field assignment outside of initializer for an instance created with Default::default()
   --> arch/src/x86_64/smbios.rs:174:9
    |
174 |         smbios_biosinfo.typ = BIOS_INFORMATION;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
note: consider initializing the variable with `x86_64::smbios::SmbiosBiosInfo { typ: BIOS_INFORMATION, length: mem::size_of::<SmbiosBiosInfo>() as u8, handle: handle, vendor: 1, version: 2, characteristics: PCI_SUPPORTED, characteristics_ext2: IS_VIRTUAL_MACHINE, ..Default::default() }` and removing relevant reassignments
   --> arch/src/x86_64/smbios.rs:173:9
    |
173 |         let mut smbios_biosinfo = SmbiosBiosInfo::default();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
f452fe7497 hypervisor: kvm: Use struct initialiser where possible
error: field assignment outside of initializer for an instance created with Default::default()
   --> hypervisor/src/kvm/mod.rs:318:9
    |
318 |         cap.cap = KVM_CAP_SPLIT_IRQCHIP;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
note: consider initializing the variable with `kvm_bindings::kvm_enable_cap { cap: KVM_CAP_SPLIT_IRQCHIP, ..Default::default() }` and removing relevant reassignments
   --> hypervisor/src/kvm/mod.rs:317:9
    |
317 |         let mut cap: kvm_enable_cap = Default::default();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
a2e1e13918 vhost_user_block: Use struct instantiation rather than mut variable
error: field assignment outside of initializer for an instance created with Default::default()
   --> vhost_user_block/src/lib.rs:223:9
    |
223 |         config.capacity = nsectors;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
note: consider initializing the variable with `block_util::VirtioBlockConfig { capacity: nsectors, blk_size: BLK_SIZE, size_max: 65535, seg_max: 128 - 2, min_io_size: 1, opt_io_size: 1, num_queues: num_queues as u16, writeback: 1, ..Default::default() }` and removing relevant reassignments
   --> vhost_user_block/src/lib.rs:221:9
    |
221 |         let mut config = VirtioBlockConfig::default();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
a4134f6b25 net_util: Remove unit error from Result
error: this returns a `Result<_, ()>
  --> net_util/src/mac.rs:68:5
   |
68 |     pub fn from_bytes(src: &[u8]) -> Result<MacAddr, ()> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `-D clippy::result-unit-err` implied by `-D warnings`
   = help: use a custom Error type instead
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err

Replace with std::io::Error like other locations in the same file.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
d2de263774 qcow: Use .contains() for range check
error: manual `!RangeInclusive::contains` implementation
   --> qcow/src/qcow.rs:412:12
    |
412 |         if cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS {
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!(MIN_CLUSTER_BITS..=MAX_CLUSTER_BITS).contains(&cluster_bits)`
    |
    = note: `-D clippy::manual-range-contains` implied by `-D warnings`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
9f2e7f455f block_util, vhost_user_block: Avoid unnecessary literal cast
error: casting integer literal to `u64` is unnecessary
  --> block_util/src/lib.rs:35:30
   |
35 | pub const SECTOR_SIZE: u64 = (0x01 as u64) << SECTOR_SHIFT;
   |                              ^^^^^^^^^^^^^ help: try: `0x01_u64`
   |
   = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Rob Bradford
faba6a3fb3 vmm: memory_manager: Use workaround for conditional function arguments
With Rust 1.49 using attributes on a function parameter is not allowed.
The recommended workaround is to put it in a new block.

error[E0658]: attributes on expressions are experimental
   --> vmm/src/memory_manager.rs:698:17
    |
698 |                 #[cfg(target_arch = "x86_64")]
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information

error: removing an expression is not supported in this position
   --> vmm/src/memory_manager.rs:698:17
    |
698 |                 #[cfg(target_arch = "x86_64")]
    |

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2021-01-04 13:46:37 +01:00
Wei Liu
2aaf0540b6 hypervisor: drop linux-loader dependency
It is not used anywhere inside the hypervisor crate.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2021-01-04 10:17:20 +00:00
dependabot-preview[bot]
d82e74e4bf build(deps): bump quote from 1.0.7 to 1.0.8
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.7 to 1.0.8.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.7...1.0.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-01 11:12:44 +00:00
dependabot-preview[bot]
f9a3476016 build(deps): bump serde_json from 1.0.60 to 1.0.61
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.60 to 1.0.61.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.60...v1.0.61)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-01 11:12:19 +00:00
dependabot-preview[bot]
aaa364a9c5 build(deps): bump openssl-sys from 0.9.59 to 0.9.60
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.59 to 0.9.60.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.59...openssl-sys-v0.9.60)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-30 07:54:34 +00:00
dependabot-preview[bot]
d20e07b271 build(deps): bump itoa from 0.4.6 to 0.4.7
Bumps [itoa](https://github.com/dtolnay/itoa) from 0.4.6 to 0.4.7.
- [Release notes](https://github.com/dtolnay/itoa/releases)
- [Commits](https://github.com/dtolnay/itoa/compare/0.4.6...0.4.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-30 07:54:24 +00:00
dependabot-preview[bot]
3074b178bd build(deps): bump rand from 0.7.3 to 0.8.0
Bumps [rand](https://github.com/rust-random/rand) from 0.7.3 to 0.8.0.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-29 20:13:28 +00:00
dependabot-preview[bot]
4751a5949d build(deps): bump syn from 1.0.54 to 1.0.56
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.54 to 1.0.56.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.54...1.0.56)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-29 19:25:53 +00:00
dependabot-preview[bot]
25d567b6c6 build(deps): bump anyhow from 1.0.35 to 1.0.37
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.35 to 1.0.37.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.35...1.0.37)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-29 19:25:41 +00:00
Rob Bradford
bb1ef5f1b8 tests: integration: Remove quiet from kernel command line
It will be useful to see kernel output from our integration tests when
they fail.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-18 16:05:14 +00:00
Rob Bradford
d9e1eec47d tests: integration: Use direct kernel boot for most tests
The boot time for direct kernel boot based tests is significantly
quicker than booting via the firmware and stock kernel as it triggers a
reboot during the boot process due to the initrd handling.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-18 16:05:14 +00:00
Rob Bradford
a4c076bd09 tests: integration: Don't always have kernel serial output
When doing a direct kernel boot only have console=ttyS0 in the command
line if we are explicitly testing the serial output. The default
behaviour is `--serial null` so this output will not be visible but will
trigger a KVM exit for every byte which is very costly when running
under nested virtualization.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-18 16:05:14 +00:00
Rob Bradford
8a27735826 main: Add thread name to log output
If there is no thread then the name is reported as "anonymous".

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-18 16:05:14 +00:00
Rob Bradford
e8c72b1b01 virtio-devices: pci: Improve debugging for activation barriers
On the CI we are seeing issues with the activation barriers not being released:

cloud-hypervisor: 12.452434193s: INFO:vmm/src/vm.rs:413 -- Waiting for barrier
cloud-hypervisor: 12.452499794s: INFO:virtio-devices/src/block.rs:382 -- Changing cache mode to writeback
cloud-hypervisor: 12.452605195s: INFO:vmm/src/vm.rs:413 -- Waiting for barrier
cloud-hypervisor: 12.452684596s: INFO:virtio-devices/src/transport/pci_device.rs:671 -- Waiting for barrier
cloud-hypervisor: 12.452708196s: INFO:virtio-devices/src/transport/pci_device.rs:673 -- Barrier released
cloud-hypervisor: 12.452717596s: INFO:vmm/src/vm.rs:415 -- Barrier released

Add some debugging to try and identify the vause of this issue.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-18 16:05:14 +00:00
Rob Bradford
ee537d9bde docs: Add instructions for using MACVTAP for bridging
Now that we can support supplying an fd to the TAP device we can support
the MACVTAP and MACVLAN use cases.

Fixes: #872

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 22:51:30 +01:00
Rob Bradford
873c79a931 tests: Add test_tap_from_fd()
This test creates new TAP device, opens it and then passed the fd in via
--net fd=<fd>

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 22:51:30 +01:00
Rob Bradford
7a51be48c6 vmm: openapi: Add fd to NetConfig definition
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 22:51:30 +01:00
Rob Bradford
7f9cdf1183 vmm: device_manager: Create a virtio-net device from TAP fd
If a TAP fd is supplied then create an virtio-net device using that TAP
fd.

Fixes: #2052

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 22:51:30 +01:00
Rob Bradford
72e5ed3372 vmm: config: Extend NetConfig to include fd for tap device
Add an "fd=" parameter to allow specifying a TAP fd to use. Currently
only one fd for one queue pair is supported.

Fixes: #2052

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 22:51:30 +01:00
Rob Bradford
ca48f1c995 virtio-devices: net: Support creating a device from a TAP fd
Add support for creating virtio-net device from existing TAP fd.
Currently only a single fd and thus no-more than 2 queues (one pair) is
suppored.

Fixes: #2052

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 22:51:30 +01:00
Rob Bradford
444905071b vmm: seccomp: Permit TUNGETIFF through the filter
This is used to obtain the TAP device name.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 22:51:30 +01:00
Rob Bradford
385d734847 net_util: Add API for creating a Tap from an fd
Ultimately this will allow the creation of a virtio-net device that is
backed by a file descriptor.

This function ensures that the TAP device is correctly setup with
offloading and non-blocking.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 22:51:30 +01:00
Rob Bradford
16c2eebfd1 net_util: Extend Tap::open_named() to take flags
This allows us to open the named TAP device without O_CLOEXEC for an
integration test.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 22:51:30 +01:00
Samuel Ortiz
21dcac7721 hypervisor: Bump iced-x86 to 1.10.0
And fix related warnings: op_kind and op_register are being deprecated
as they might panic.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 18:58:34 +00:00
Rob Bradford
3ee56112cb tests: Bump boot default timeout
Starting the virtio device threads from the VMM thread has slowed down
the start of the VM when running on a highly contested system like the
CI.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 11:23:53 +00:00
Rob Bradford
03db48306b vmm: Activate virtio device from VMM thread
When a device is ready to be activated signal to the VMM thread via an
EventFd that there is a device to be activated. When the VMM receives a
notification on the EventFd that there is a device to be activated
notify the device manager to attempt to activate any devices that have
not been activated.

As a side effect the VMM thread will create the virtio device threads.

Fixes: #1863

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 11:23:53 +00:00
Rob Bradford
dee42ebb29 virtio-devices: pci_device: Split out device activation
This can then be used to activate the device from a separate thread.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 11:23:53 +00:00
Rob Bradford
7cc729c7d9 pci, virtio-devices: Extend barrier returning through PCI code
We need to be able to return the barrier from the code that prepares to
activate the virtio device. This triggered by a write to the
configuration fields stored in the PCI BAR. Since bars can be accessed
by both memory mapping and through PCI config I/O several prototypes
must be changed.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 11:23:53 +00:00
Rob Bradford
a8643dc523 vm-device, vmm: Wait for barrier if one is returned
Wait for the barrier if one is provided by the result of the MMIO and
PIO write.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 11:23:53 +00:00
Rob Bradford
1fc6d50f3e misc: Make Bus::write() return an Option<Arc<Barrier>>
This can be uses to indicate to the caller that it should wait on the
barrier before returning as there is some asynchronous activity
triggered by the write which requires the KVM exit to block until it's
completed.

This is useful for having vCPU thread wait for the VMM thread to proceed
to activate the virtio devices.

See #1863

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 11:23:53 +00:00
Rob Bradford
dbf4a252ad devices: acpi: Remove empty BusDevice::write implementation
The trait already includes an empty implementation of this function.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-17 11:23:53 +00:00
dependabot-preview[bot]
add9b13497 build(deps): bump signal-hook from 0.2.1 to 0.2.2
Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.2.1 to 0.2.2.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/compare/v0.2.1...v0.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-15 18:03:49 +00:00
dependabot-preview[bot]
3d097eef44 build(deps): bump arc-swap from 1.0.0 to 1.1.0
Bumps [arc-swap](https://github.com/vorner/arc-swap) from 1.0.0 to 1.1.0.
- [Release notes](https://github.com/vorner/arc-swap/releases)
- [Changelog](https://github.com/vorner/arc-swap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/arc-swap/compare/v1.0.0...v1.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-14 09:13:04 +00:00
Muminul Islam
8c85dd32fa hypervisor: Move msr and msr_data macro to arch/x86
Currently these two macros(msr, msr_data) reside both on kvm and mshv
module. Definition is same for both module. Moving them to arch/x86
module eliminates redundancy and makes more sense.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-11 00:59:46 +01:00
Muminul Islam
aac86f4523 hypervisor: Fix clippy errors in the mshv module
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-10 19:43:58 +00:00
dependabot-preview[bot]
f3e889c204 build(deps): bump vcpkg from 0.2.10 to 0.2.11
Bumps [vcpkg](https://github.com/mcgoo/vcpkg-rs) from 0.2.10 to 0.2.11.
- [Release notes](https://github.com/mcgoo/vcpkg-rs/releases)
- [Changelog](https://github.com/mcgoo/vcpkg-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mcgoo/vcpkg-rs/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-10 19:19:55 +00:00
Rob Bradford
3eb9e61a32 rpm: Update spec file for version bump
Update the spec file for the rpm to the latest version.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-10 18:13:59 +00:00
Rob Bradford
d42b50845b build, release-notes.md: Document 0.12.0 release
Update release notes and version number for the new release.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-10 18:11:56 +00:00
Rob Bradford
06f391e022 tests: integration: Retry epoll if we receive -EINTR or -EAGAIN
On the CI we are seeing that sometimes the epoll is receiving these
errors which do not indicate a failure but that we should retry.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-10 10:11:19 -08:00
dependabot-preview[bot]
60d2469e7a build(deps): bump openssl-sys from 0.9.58 to 0.9.59
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.58 to 0.9.59.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.58...openssl-sys-v0.9.59)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-10 07:33:54 +00:00
Wei Liu
c4f8e4b000 main: provide a sensible error message when /dev/mshv is missing
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-09 17:28:36 +00:00
Muminul Islam
c32959261e gh: Expand clippy tests to cover the mshv feature
Since we can't test mshv and kvm at the same time, --all-features no
longer work.
We factorize all, non-hypervisor related features into a common set and
mix that with either mshv and kvm.

Co-Developed-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Co-Developed-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
ef3fad8388 hypervisor: mshv: Implement CPU state for MshvVcpu
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
2b42f6c835 hypervisor: mhsv: Implement CPU run loop
Handle CPU exits, adding instruction emulations.
Keep CPU specific data inside vmm for later use.

Co-Developed-by: Nuno Das Neves <nudasnev@microsoft.com>
Signed-off-by: Nuno Das Neves <nudasnev@microsoft.com>
Co-Developed-by: Praveen Paladugu <prapal@microsoft.com>
Signed-off-by: Praveen Paladugu <prapal@microsoft.com>
Co-Developed-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Co-Developed-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
7acb5c6437 hypervisor, mshv: Define MshvEmulatorContext
This patch adds the definition and implementation
MshvEmulatorContext which is platform emulation for Hyper-V.

Co-Developed-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Co-Developed-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
63f356b75b hypervisor: mhsv: Define software emulated TLB
A software emulated TLB. This is mostly used by
the instruction emulator to cache gva to gpa
translations passed from the hypervisor.

Co-Developed-by: Nuno Das Neves <nudasnev@microsoft.com>
Signed-off-by: Nuno Das Neves <nudasnev@microsoft.com>
Co-Developed-by: Praveen Paladugu <prapal@microsoft.com>
Signed-off-by: Praveen Paladugu <prapal@microsoft.com>
Co-Developed-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Co-Developed-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
fd0ef6cfb5 hypervisor: mshv: Emulate IrqFd and IOEventFd for mshv module
We don't have IrqFd and IOEventFd support in the kernel for now.
So an emulation layer is needed. In the future, we will be adding this
support in the kernel.

Co-Developed-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
286a23fbd4 hypervisor: mshv: Add vmmops to MshvVm struct
vmmops trait object is needed to get access some
of the upper level vmm functionalities i.e guest
memory access, IO read write etc.

Co-Developed-by: Praveen Paladugu <prapal@microsoft.com>
Signed-off-by: Praveen Paladugu <prapal@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
3a93487b82 hypervisor: mshv: Add Microsoft Hypervisor specific data to Mshv{Vm,Vcpu}
Adding hv_state (hyperv state) to Vm and Vcpu struct for mshv.
This state is needed to keep some kernel data(for now hypercall page)
in the vmm.

Co-Developed-by: Praveen Paladugu <prapal@microsoft.com>
Signed-off-by: Praveen Paladugu <prapal@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
23c46b162e hypervisor, vmm: Implement the mshv module and add mshv support in vmm
Implement hypervisor, Vm, Vcpu crate at a minimal
functionalities. Also adds the mshv feature gate,
separates out the functionalities between kvm and
mshv inside the vmm crate.

Co-Developed-by: Nuno Das Neves <nudasnev@microsoft.com>
Signed-off-by: Nuno Das Neves <nudasnev@microsoft.com>
Co-Developed-by: Praveen Paladugu <prapal@microsoft.com>
Signed-off-by: Praveen Paladugu <prapal@microsoft.com>
Co-Developed-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Co-Developed-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
f4af668d76 hypervisor, vmm: Implement MsiInterruptOps for mshv
Co-Developed-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
7fe5d276a3 hypervisor: mshv: Add x86_64 module
Add x86_64 specific definitions as we only plan
to support x86_64 for the moment.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
e664fa386b hypervisor: Modify toml file to add mshv-{bindings, ioctls) crates
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
9919dec1d9 hypervisor: Introduce mshv module in the hypervisor
This is the initial folder structure of the mshv module inside
the hypervisor crate. The aim of this module is to support Microsoft
Hyper-V as a supported Hypervisor.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
Muminul Islam
9ce6c3b75c hypervisor, vmm: Feature guard KVM specific code
There are some code base and function which are purely KVM specific for
now and we don't have those supports in mshv at the moment but we have plan
for the future. We are doing a feature guard with KVM. For example, KVM has
mp_state, cpu clock support,  which we don't have for mshv. In order to build
those code we are making the code base for KVM specific compilation.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-09 14:55:20 +01:00
dependabot-preview[bot]
47f59409db build(deps): bump anyhow from 1.0.34 to 1.0.35
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.34 to 1.0.35.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.34...1.0.35)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-08 09:01:03 +00:00
dependabot-preview[bot]
b51f112629 build(deps): bump libc from 0.2.80 to 0.2.81
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.80 to 0.2.81.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.80...0.2.81)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-08 08:13:51 +00:00
dependabot-preview[bot]
d158fa8de2 build(deps): bump linux-loader from 3cf96c5 to acf9c21
Bumps [linux-loader](https://github.com/rust-vmm/linux-loader) from `3cf96c5` to `acf9c21`.
- [Release notes](https://github.com/rust-vmm/linux-loader/releases)
- [Commits](3cf96c53ed...acf9c21591)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-08 08:13:27 +00:00
dependabot-preview[bot]
c64de38140 build(deps): bump syn from 1.0.53 to 1.0.54
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.53 to 1.0.54.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.53...1.0.54)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-07 22:10:21 +00:00
Rob Bradford
9594ecec0d build: Only pull in iced-x86 for x86_64 builds
Fixes: #2024

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-07 17:42:42 +00:00
Anatol Belski
6c0c47a530 docs/windows.md: Add explicit note on how to start WinDbg
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2020-12-07 16:56:00 +00:00
Wei Liu
6d38612f6c hypervisor: x86: move RFLAGS bits to regs.rs
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-07 14:35:35 +01:00
Wei Liu
a44d96c9cc hypervisor: emulator: switch to use vec in MockVMM
The customized hashmap macro can't be lifted to common MockVMM code.
MockVMM only needs a collection to iterate over to get initial register
states. A vector is just as good as a hashmap.

Switch to use a vector to store initial register states. This allows us
to drop the hashmap macro everywhere.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-07 14:35:35 +01:00
Wei Liu
93b7dcac12 hypervisor: emulator: emulate CMP
Unfortunately Rust stable does not yet have inline ASM support the flag
calculation will have to be implemented in software.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-07 14:35:35 +01:00
Wei Liu
dd3844a892 hypervisor: emulator: drop imm_op macro
It is not needed anymore.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-07 14:35:35 +01:00
Wei Liu
0c3ef986fa hypervisor: emulator: rewrite MOV emulation with get/set_op
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-07 14:35:35 +01:00
Wei Liu
53c3b00fb0 hypervisor: emulator: introduce get/set_op
Provide two helpers to get and set operands. This reduces repetition.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-07 14:35:35 +01:00
Rob Bradford
03b824be0b build: Disable fast failing for build actions
nightly / beta are not required actions but if they fail then all other
builds will be cancelled and so prevent the PR from being cleared as
buildable.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-07 12:36:38 +00:00
dependabot-preview[bot]
558975aa51 build(deps): bump serde_derive from 1.0.117 to 1.0.118
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.117 to 1.0.118.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.117...v1.0.118)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-07 09:22:13 +00:00
dependabot-preview[bot]
d78b9bd287 build(deps): bump smallvec from 1.5.0 to 1.5.1
Bumps [smallvec](https://github.com/servo/rust-smallvec) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/compare/v1.5.0...v1.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-07 09:08:31 +00:00
dependabot-preview[bot]
365d39ca2c build(deps): bump serde from 1.0.117 to 1.0.118
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.117 to 1.0.118.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.117...v1.0.118)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-07 08:36:52 +00:00
Muminul Islam
d79594aa3f scripts: dev_cli: Remove unnecessary spaces
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-05 00:01:41 +01:00
Muminul Islam
27b5f8d7e3 scripts: Add an option to accept hypervisor argument
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-12-05 00:01:41 +01:00
Rob Bradford
2cc312b65c build: Fix cargo fuzz build
Unfortunately it seems patch entries are ignored when obtaining
dependencies from another workspace.

Remove the problematic kvm-ioctls and kvm-bindings patch entries and use
the forked repository directly.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-04 11:38:44 +00:00
dependabot-preview[bot]
d416271457 build(deps): bump cc from 1.0.65 to 1.0.66
Bumps [cc](https://github.com/alexcrichton/cc-rs) from 1.0.65 to 1.0.66.
- [Release notes](https://github.com/alexcrichton/cc-rs/releases)
- [Commits](https://github.com/alexcrichton/cc-rs/compare/1.0.65...1.0.66)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-04 10:51:49 +00:00
Rob Bradford
9e6a023f7f vmm: Don't continue to lookup debug I/O port
When using an PIO write to 0x80 which is a special case handle that and
then return without going through the resolve.

This removes an extra warning that is reported.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-03 07:25:42 -08:00
Wei Liu
90ae4676c8 hypervisor: x86: emulator: drop unneeded curly brackets
There is no need to have a pair of curly brackets for structures without
any member.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-03 14:52:00 +01:00
dependabot-preview[bot]
89bc413add build(deps): bump serde_json from 1.0.59 to 1.0.60
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.59 to 1.0.60.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.59...v1.0.60)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-03 13:50:29 +00:00
Wei Liu
c352b59857 hypervisor: emulator: drop InstructionMap
It is not needed anymore.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-02 19:45:36 +01:00
Wei Liu
ab89b48143 hypervisor: emulator: use static mapping to dispatch emulation
The mapping between code and its handler is static. We can drop the
HashMap in favour of a static match expression.

This has two benefits:
1. No more memory allocation and deallocation for the HashMap.
2. Shorter look-up time.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-12-02 19:45:36 +01:00
Rob Bradford
ffaab46934 misc: Use a more relaxed memory model when possible
When a total ordering between multiple atomic variables is not required
then use Ordering::Acquire with atomic loads and Ordering::Release with
atomic stores.

This will improve performance as this does not require a memory fence
on x86_64 which Ordering::SeqCst will use.

Add a comment to the code in the vCPU handling code where it operates on
multiple atomics to explain why Ordering::SeqCst is required.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-02 19:04:30 +01:00
Rob Bradford
280d4fb245 vmm: Include device tree in vm.info API
The DeviceNode cannot be fully represented as it embeds a Rust style
enum (i.e. with data) which is instead represented by a simple
associative array.

Fixes: #1167

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-01 16:44:25 +01:00
Samuel Ortiz
15f0451c34 hypervisor: emulator: Format instructions on error paths
Formatting instructions might be costly, so we only want to do that on
the emulation error paths.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-12-01 11:13:54 +01:00
Samuel Ortiz
3fa6e17cc4 hypervisor: x86: Make imm_op available to all emulated instructions
By moving it to the instructions root module.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-12-01 11:13:54 +01:00
Wei Liu
e2c81f9ed8 hypervisor: x86: Extend the imm_op() macro
To support every kind of immediate operands.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-12-01 11:13:54 +01:00
Rob Bradford
ead8453120 build: Remove vhost_user_fs
This has been superseded by virtiofsd-rs.

Fixes: #2013

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-01 11:13:21 +01:00
Rob Bradford
c4ebe83576 rpm: Update .spec file to not reference vhost_user_fs
Fixes: #2013

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-01 11:13:21 +01:00
Rob Bradford
1908f488c3 tests: Move tests from vhost_user_fs to virtiofsd-rs
Download and build virtiofsd-rs and then use that in the integration
test suite.

Fixes: #2013

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-01 11:13:21 +01:00
Rob Bradford
593a958fe5 pci, vmm: Include VFIO devices in device tree
Fixes: #1687

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-12-01 10:49:04 +01:00
Wei Liu
ba71390d6a hypervisor: drop arc-swap dependency
It is no longer needed after 0fec32658.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-11-30 16:47:32 +00:00
Samuel Ortiz
f6892f2ede hypervisor: emulator: Add a negative instruction fetch unit test
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-30 13:09:19 +00:00
Samuel Ortiz
251434862f hypervisor: emulator: Fix logic bug in MockVMM
emulate_first_insn() really means we want only the first instruction to
be emulated.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-30 13:09:19 +00:00
Samuel Ortiz
1ac4c42a84 hypervisor: emulator: Add initial fetch support
When the x86 instruction decoder tells us about some missing bytes from
the instruction stream, we call into the platform fetch method and
emulate one last instruction.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-30 13:09:19 +00:00
Samuel Ortiz
7dc92d1dac hypervisor: emulator: Decoding loop restructuring
In preparation for the instruction fetching step, we modify the decoding
loop so that we can check what the last decoding error is.

We also switch to explictly using decode_out() which removes a 32 bytes
copy compared to decode().

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-30 13:09:19 +00:00
Samuel Ortiz
7a4edecd29 hypervisor: x86: Add an address linearization method to CpuStateManager
From a CPU state and a segment, we can translate a logical (segmented)
address into a linear one.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-30 13:09:19 +00:00
Samuel Ortiz
d419e30df1 hypervisor: x86: Add a SegmentRegistorOps trait
In order to validate emulated memory accesses, we need to be able to get
all the segments descriptor attributes.

This is done by abstracting the SegmentRegister attributes through a
trait that each hypervisor will have to implement.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-30 13:09:19 +00:00
Samuel Ortiz
3faffcb087 hypervisor: emulator: Add a CPU mode getter to CpuStateManager
We need to be able to build the CPU mode from its state in order to
start implementing mode related checks in the x86 emulator.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-30 13:09:19 +00:00
Samuel Ortiz
b3a1f5f1be hypervisor: Start moving register definitions to the hypervisor crate
Most of arch/src/x86_64/regs.rs will eventually move unde
hypervisor/src/x86/regs.rs.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-30 13:09:19 +00:00
dependabot-preview[bot]
2bc5119730 build(deps): bump syn from 1.0.52 to 1.0.53
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.52 to 1.0.53.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.52...1.0.53)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-30 09:09:26 +00:00
dependabot-preview[bot]
5100bc94cc build(deps): bump syn from 1.0.51 to 1.0.52
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.51 to 1.0.52.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.51...1.0.52)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-28 00:28:08 +00:00
dependabot-preview[bot]
6bf9fe487b build(deps): bump pnet from 0.26.0 to 0.27.2
Bumps [pnet](https://github.com/libpnet/libpnet) from 0.26.0 to 0.27.2.
- [Release notes](https://github.com/libpnet/libpnet/releases)
- [Commits](https://github.com/libpnet/libpnet/compare/v0.26.0...v0.27.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-27 23:56:02 +00:00
Rob Bradford
d1a23d9085 docs: Add instructions for using perf
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-27 19:19:14 +01:00
Rob Bradford
05f785fa88 build: Move timeouts to integration test blocks
If we rely on timeouts at the top level we can get builds being aborted
simply because they took too long to be scheduled rather than because
the actual integration tests took too long.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-27 19:18:39 +01:00
Samuel Ortiz
a3d957ba4d hypervisor: emulator: Clean the MockVMM initialization path
Separate the standalone _init_and_run() function into 2 MockVMM methods:
new() and emulate_insn().

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-27 19:17:35 +01:00
Samuel Ortiz
5bd63efa45 hypervisor: emulator: Move MockVMM into a shared test module
The MockVMM platform will be used by other instructions emulation
implementations, but also by the emulator framework.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-27 19:17:35 +01:00
Wei Liu
66b001665f hypervisor: x86: only calculate address when necessary in MOV emulation
Only calculate the address when the operand is memory.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-11-26 16:12:23 +01:00
Wei Liu
c6aea5af5d hypervisor: x86: drop an extraneous box indirection
There is no need to put a box into another box.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-11-26 12:02:47 +00:00
Wei Liu
c8b655490e hypervisor: x86: reference PlatformEmulator in Emulator
The observation here is PlatformEmulator can be seen as the context for
emulation to take place. It should be rather easy to construct a context
that satisfies the lifetime constraints for instruction emulation.

The thread doing the emulation will have full ownership over the
context, so this removes the need to wrap PlatformEmulator in Arc and
Mutex, as well as the need for the context to be either Clone or Copy.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-11-26 12:14:40 +01:00
Rob Bradford
72741c557a actions: Add "--all --tests" to clippy in quality action
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-26 09:32:46 +01:00
Rob Bradford
b2608ca285 vmm: cpu: Fix clippy issues inside test
Found by:  cargo clippy --all-features --all --tests

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-26 09:32:46 +01:00
Rob Bradford
74fe032827 vm-device: bus: Fix clippy issues inside tests
Found by:  cargo clippy --all-features --all --tests

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-26 09:32:46 +01:00
Rob Bradford
40a2e3b462 vhost_user_block: Fix clippy issue
Found by:  cargo clippy --all-features --all --tests

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-26 09:32:46 +01:00
Rob Bradford
abc6672f21 qcow: Fix clippy issues inside tests
Found by:  cargo clippy --all-features --all --tests

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-26 09:32:46 +01:00
Rob Bradford
053c9e4d73 devices: serial: Fix clippy issues inside tests
Found by:  cargo clippy --all-features --all --tests

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-26 09:32:46 +01:00
Rob Bradford
9ea19852b5 arch: x86_64: Fix clippy issues inside tests
Found by:  cargo clippy --all-features --all --tests

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-26 09:32:46 +01:00
Rob Bradford
9871ff454b arch_gen: mpspec: Fix clippy issues inside tests
Found by:  cargo clippy --all-features --all --tests

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-26 09:32:46 +01:00
dependabot-preview[bot]
e817b4f36c build(deps): bump crossbeam-utils from 0.8.0 to 0.8.1
Bumps [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-utils-0.8.0...crossbeam-utils-0.8.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-26 08:32:02 +00:00
Rob Bradford
6f63cd00eb vmm: device_manager: Remove ActivatedBackend struct
This was used for vhost-user self spawning but no longer has any users.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-25 20:47:43 +01:00
Wei Liu
a6ad85dc40 hypervisor: x86: handle more registers in emulator
List all GPRs documented in Intel's SDM.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-11-25 20:18:40 +01:00
Samuel Ortiz
0f4f30dbde hypervisor: x86: Add MOV to the emulator instruction map
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-25 17:02:11 +01:00
Samuel Ortiz
fe5401223b hypervisor: x86: Emulate MOV
And add a few unit tests based on a Mock platform.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-25 17:02:11 +01:00
Samuel Ortiz
12b7a498d9 hypervisor: x86: Add an instruction emulator
The emulator gets a CPU state from a CpuStateManager instance, emulates
the passed instructions stream and returns the modified CPU state.

The emulator is a skeleton for now since it comes with an empty
instruction mnemonic map.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-25 17:02:11 +01:00
Samuel Ortiz
1fc97e91a4 hypervisor: x86: Add an InstructionHandler interface
And an InstructionMap helper structure to map x86 mnemonic codes
to instruction handlers.

Any instruction emulation implementation should then boil down with
implementing InstructionHandler for any supported mnemonic.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-25 17:02:11 +01:00
Samuel Ortiz
fc5d6c96be hypervisor: x86: Add a minimal CpuStateManager implementation
Minimal will be defined by the amount of emulated instructions.
Carrying all GPRs, all CRs, segment registers and table registers should
cover quite a few instructions.

Co-developed-by: Wei Liu <liuwe@microsoft.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-25 17:02:11 +01:00
Samuel Ortiz
546778ebfb hypervisor: x86: Add a CpuStateManager interface
For efficiently emulating x86 instructions, we need to build and pass a
CPU state copy/reference to instruction emulation handlers. Those handlers
will typically modify the CPU state and let the caller commit those
changes back through the PlatformEmulator trait set_cpu_state method.

Hypervisors typically have internal CPU state structures, that maps back
to the correspinding kernel APIs. By implementing the CpuState trait,
instruction emulators will be able to directly work on CPU state
instances that are directly consumable by the underlying hypervisor and
its kernel APIs.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-25 17:02:11 +01:00
Samuel Ortiz
f0360aff83 hypervisor: Architecture agnostic instruction emulation interface
In order to emulate instructions, we need a way to get access to some of
the guest resources. The PlatformEmulator interface provides guest
memory and CPU state access to emulator implementations.

Typically, an hypervisor will implement PlatformEmulator for architecture
specific instruction emulators to build their framework on top of.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-25 17:02:11 +01:00
Samuel Ortiz
ae96aeda65 arch: Move the gdt module to the hypervisor crate
We will need the GDT API for the hypervisor's x86 instruction
emulator implementation, it's better if the arch crate depends on the
hypervisor one rather than the other way around.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-25 17:02:11 +01:00
Rob Bradford
b5b97f7b05 vmm: When receiving a migration store the config
The configuration is stored separately to the Vm in the VMM. The failure
to store the config was preventing the VM from shutting down correctly
as Vmm::vm_delete() checks for the presence of the config.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-25 01:27:26 +01:00
Rob Bradford
9fc2613b41 vm-device: bus: Remove unwrap() when upgrading weak reference
Rather return the None to the caller to handle instead. This removes the
source of a potential panic.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-25 01:27:26 +01:00
Rob Bradford
44b5a19209 bin: ch-remote: Fix typo in error message
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-25 01:27:26 +01:00
Rob Bradford
df6b52924f vmm: Unlink created socket after source connects
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-25 01:27:26 +01:00
Rob Bradford
1ab1341775 vmm: seccomp_filters: Add KVM_GET_DIRTY_LOG to permitted calls
The live migration support added use of this ioctl but it wasn't
included in the permitted list.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-25 01:27:26 +01:00
dependabot-preview[bot]
a29f7ffcc7 build(deps): bump signal-hook from 0.2.0 to 0.2.1
Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.2.0 to 0.2.1.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-25 00:26:27 +00:00
dependabot-preview[bot]
eda39d0469 build(deps): bump rust-argon2 from 0.8.2 to 0.8.3
Bumps [rust-argon2](https://github.com/sru-systems/rust-argon2) from 0.8.2 to 0.8.3.
- [Release notes](https://github.com/sru-systems/rust-argon2/releases)
- [Changelog](https://github.com/sru-systems/rust-argon2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sru-systems/rust-argon2/compare/0.8.2...0.8.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-24 09:38:49 +00:00
dependabot-preview[bot]
de0c24810e build(deps): bump syn from 1.0.50 to 1.0.51
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.50 to 1.0.51.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.50...1.0.51)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-24 06:01:35 +00:00
Samuel Ortiz
fadeb98c67 cargo: Bulk update
Includes updates for ssh2, cc, syn, tinyvec, backtrace micro-http and libssh2.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-23 12:25:31 +01:00
Samuel Ortiz
1fc0b94fcd cargo: Move to crates.io vm-memory 0.4.0
vm-memory 0.4.0 now contains all our fixes.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-23 10:55:13 +01:00
Samuel Ortiz
ef4c896bbf arch: aarch64: Fix rust 1.48 clippy warnings
methods called `new` usually return `Self`
See https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-20 17:09:28 +01:00
Samuel Ortiz
72bb255ff6 pci, virtio-devices: Fix rust 1.48 clippy warnings
Unnecessary closure used to substitute value for `Option::None`
See https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-20 17:09:28 +01:00
Samuel Ortiz
a406d90059 arch: Fix rust 1.48 clippy warnings
const should not be mutable types:
https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-11-20 17:09:28 +01:00
dependabot-preview[bot]
0dfffee6ba build(deps): bump cc from 1.0.62 to 1.0.63
Bumps [cc](https://github.com/alexcrichton/cc-rs) from 1.0.62 to 1.0.63.
- [Release notes](https://github.com/alexcrichton/cc-rs/releases)
- [Commits](https://github.com/alexcrichton/cc-rs/compare/1.0.62...1.0.63)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-18 23:37:01 +00:00
Rob Bradford
0fec326582 hypervisor, vmm: Remove shared ownership of VmmOps
This interface is used by the vCPU thread to delegate responsibility for
handling MMIO/PIO operations and to support different approaches than a
VM exit.

During profiling I found that we were spending 13.75% of the boot CPU
uage acquiring access to the object holding the VmmOps via
ArcSwap::load_full()

    13.75%     6.02%  vcpu0            cloud-hypervisor    [.] arc_swap::ArcSwapAny<T,S>::load_full
            |
            ---arc_swap::ArcSwapAny<T,S>::load_full
               |
                --13.43%--<hypervisor::kvm::KvmVcpu as hypervisor::cpu::Vcpu>::run
                          std::sys_common::backtrace::__rust_begin_short_backtrace
                          core::ops::function::FnOnce::call_once{{vtable-shim}}
                          std::sys::unix::thread::Thread::new::thread_start

However since the object implementing VmmOps does not need to be mutable
and it is only used from the vCPU side we can change the ownership to
being a simple Arc<> that is passed in when calling create_vcpu().

This completely removes the above CPU usage from subsequent profiles.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-19 00:16:02 +01:00
dependabot-preview[bot]
6a591ca81d build(deps): bump pin-project from 1.0.1 to 1.0.2
Bumps [pin-project](https://github.com/taiki-e/pin-project) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/taiki-e/pin-project/releases)
- [Changelog](https://github.com/taiki-e/pin-project/blob/master/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/pin-project/compare/v1.0.1...v1.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-18 22:15:58 +00:00
dependabot-preview[bot]
a525f8365f build(deps): bump unicode-normalization from 0.1.15 to 0.1.16
Bumps [unicode-normalization](https://github.com/unicode-rs/unicode-normalization) from 0.1.15 to 0.1.16.
- [Release notes](https://github.com/unicode-rs/unicode-normalization/releases)
- [Commits](https://github.com/unicode-rs/unicode-normalization/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-18 22:15:49 +00:00
Rob Bradford
f271378811 tests: Wait for reboot in test_virtio_watchdog()
Aim to reduce flaky failures of this test by waiting for the VM to come
back after a reboot.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-18 18:27:34 +01:00
Rob Bradford
78b9edf848 tests: Get output from firmware and Linux for test_large_vm()
As we switched to focal for this test we no longer get any output during
the boot unless serial is used over virtio-console.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-18 18:27:34 +01:00
Rob Bradford
d0aebfb922 tests: Increase time waited for hotplugged memory to appear
There have been a lot of flakes around tests such as
test_virtio_fs_hotplug_dax_on_w_vhost_user_fs_daemon() or
test_virtio_fs_hotplug_dax_on() which all try and hotplug memory.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-18 18:27:34 +01:00
Rob Bradford
f913dd6150 tests: Add integration test to force io_uring off for block
Fixes: #1561

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-18 11:47:54 +01:00
Rob Bradford
c622278030 config, device_manager: Add support for disabling io_uring for testing
Add config parameter to --disk called "_disable_io_uring" (the
underscore prefix indicating it is not for public consumpion.) Use this
option to disable io_uring if it would otherwise be used.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-18 11:47:54 +01:00
Rob Bradford
d1d0421103 main: Remove --net-backend and --block-backend from cloud-hypervisor
Remove the parameters used for self spawning from the cloud-hypervisor
binary.

See: #1925

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-18 11:46:32 +01:00
Rob Bradford
fb70baece0 tests: Make integration test use vhost_user_{net,block} binaries
With the removal of vhost-user self-spawning support we should migrate
the tests to use the binaries so that we can remove the functionality
from the cloud-hypervisor binary itself.

See: #1925

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-18 11:46:32 +01:00
dependabot-preview[bot]
0159ab93e1 build(deps): bump unicode-normalization from 0.1.14 to 0.1.15
Bumps [unicode-normalization](https://github.com/unicode-rs/unicode-normalization) from 0.1.14 to 0.1.15.
- [Release notes](https://github.com/unicode-rs/unicode-normalization/releases)
- [Commits](https://github.com/unicode-rs/unicode-normalization/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-18 09:01:29 +00:00
dependabot-preview[bot]
609d704168 build(deps): bump smallvec from 1.4.2 to 1.5.0
Bumps [smallvec](https://github.com/servo/rust-smallvec) from 1.4.2 to 1.5.0.
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/compare/v1.4.2...v1.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-17 21:56:51 +00:00
Rob Bradford
3ac9b6c404 vmm: Implement live migration
Now the VM is paused/resumed by the migration process itself.

0. The guest configuration is sent to the destination
1. Dirty page log tracking is started by start_memory_dirty_log()
2. All guest memory is sent to the destination
3. Up to 5 attempts are made to send the dirty guest memory to the
   destination...
4. ...before the VM is paused
5. One last set of dirty pages is sent to the destination
6. The guest is snapshotted and sent to the destination
7. When the migration is completed the destination unpauses the received
   VM.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-17 16:57:11 +00:00
Rob Bradford
b34703d29f vmm: vm: Add dirty log related passthrough methods
This allows code running in the VMM to access the VM's MemoryManager's
functionality for managing the dirty log including resetting it but also
generating a table.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-17 16:57:11 +00:00
Rob Bradford
cf6763dfdb vmm: migration: Add missing response check
A read and check of the response was missing from when sending the
memory to the destination.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-17 16:57:11 +00:00
Rob Bradford
11a69450ba vm-migration, vmm: Send configuration in separate step
Prior to sending the memory the full state is not needed only the
configuration. This is sufficient to create the appropriate structures
in the guest and have the memory allocations ready for filling.

Update the protocol documentation to add a separate config step and move
the state to after the memory is transferred. As the VM is created in a
separate step to restoring it the requires a slightly different
constructor as well as saving the VM object for the subsequent commands.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-17 16:57:11 +00:00
Rob Bradford
c62e409827 memory_manager: Generate a MemoryRangeTable for dirty ranges
In order to do this we must extend the MemoryManager API to add the
ability to specify the tracking of the dirty pages when creating the
userspace mappings and also keep track of the userspace mappings that
have been created for RAM regions.

Currently the dirty pages are collected into ranges based on a block
level of 64 pages. The algorithm could be tweaked to create smaller
ranges but for now if any page in the block of 64 is dirty the whole
block is added to the range.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-17 16:57:11 +00:00
Rob Bradford
041724a7cf hypervisor: Add ability to get dirty logged pages
Return a bitmap of pages that have been dirtied (written to) since it
was last called.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-17 16:57:11 +00:00
Rob Bradford
8baa244ec1 hypervisor: Add control for dirty page logging
When creating a userspace mapping provide a control for enabling the
logging of dirty pages.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-17 16:57:11 +00:00
Anatol Belski
b399287430 memory_manager: Make addressable space size 64k aligned
While the addressable space size reduction of 4k in necessary due to
the Linux bug, the 64k alignment of the addressable space size is
required by Windows. This patch satisfies both.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2020-11-16 16:39:11 +00:00
Rob Bradford
c0827e01b1 build: Update arc-swap dependency to 1.0.0
Also bump the vm-memory version to one that depends on arc-swap 1.0.0
too.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-16 14:10:09 +01:00
Rob Bradford
46e736973e hypervisor: kvm: Correctly share VmmOps between Kvm{Vm,Vcpu}
Cloning the ArcSwapOption (like the ArcSwap) does not act like a
.clone() on an Arc, instead an entirely new ArcSwap is created with the
same contents. To correctly share the ArcSwap needs to be placed inside
an Arc.

See: 2433d5719b (diff-6c6d94533c44c19bd1416ef17bad1a878e63dca6e98d59181228fbe8f967c62bR6)

Due to this being wrongly used ::clone() was removed from
ArcSwap/ArcSwapOption in 1.0.0.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-16 14:10:09 +01:00
dependabot-preview[bot]
096d99664d build(deps): bump ssh2 from 0.8.2 to 0.8.3
Bumps [ssh2](https://github.com/alexcrichton/ssh2-rs) from 0.8.2 to 0.8.3.
- [Release notes](https://github.com/alexcrichton/ssh2-rs/releases)
- [Commits](https://github.com/alexcrichton/ssh2-rs/compare/0.8.2...0.8.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-15 16:01:54 +00:00
dependabot-preview[bot]
f7c42dc7d2 build(deps): bump vmm-sys-util from 0.6.1 to 0.7.0
Bumps [vmm-sys-util](https://github.com/rust-vmm/vmm-sys-util) from 0.6.1 to 0.7.0.
- [Release notes](https://github.com/rust-vmm/vmm-sys-util/releases)
- [Changelog](https://github.com/rust-vmm/vmm-sys-util/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-vmm/vmm-sys-util/compare/v0.6.1...v0.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-13 15:55:05 +00:00
dependabot-preview[bot]
37f947d614 build(deps): bump unicode-normalization from 0.1.13 to 0.1.14
Bumps [unicode-normalization](https://github.com/unicode-rs/unicode-normalization) from 0.1.13 to 0.1.14.
- [Release notes](https://github.com/unicode-rs/unicode-normalization/releases)
- [Commits](https://github.com/unicode-rs/unicode-normalization/compare/v0.1.13...v0.1.14)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-12 16:18:48 +00:00
dependabot-preview[bot]
47d2421e39 build(deps): bump once_cell from 1.5.1 to 1.5.2
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.5.1...v1.5.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-12 14:02:38 +00:00
dependabot-preview[bot]
04419065a3 build(deps): bump once_cell from 1.5.0 to 1.5.1
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.5.0...v1.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-11 11:19:21 +00:00
Rob Bradford
ca60adda70 vmm: Add support for sending and receiving migration if VM is paused
This is tested by:

Source VMM:

target/debug/cloud-hypervisor --kernel ~/src/linux/vmlinux \
--pmem file=~/workloads/focal.raw --cpus boot=1 \
--memory size=2048M \
--cmdline"root=/dev/pmem0p1 console=ttyS0" --serial tty --console off \
--api-socket=/tmp/api1 -v

Destination VMM:

target/debug/cloud-hypervisor --api-socket=/tmp/api2 -v

And the following commands:

target/debug/ch-remote --api-socket=/tmp/api1 pause
target/debug/ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/foo &
target/debug/ch-remote --api-socket=/tmp/api1 send-migration unix:/tmp/foo
target/debug/ch-remote --api-socket=/tmp/api2 resume

The VM is then responsive on the destination VMM.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-11 11:07:24 +01:00
Rob Bradford
dfe2dadb3e vmm: memory_manager: Make the snapshot source directory an Option
This allows the code to be reused when creating the VM from a snapshot
when doing VM migration.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-11 11:07:24 +01:00
Rob Bradford
523029449e bin: ch-remote: Add support for receive/send migration commands
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-11 11:07:24 +01:00
Rob Bradford
7ac764518c vmm: api: Implement API support for migration
Add API entry points with stub implementation for sending and receiving
a VM from one VMM to another.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-11 11:07:24 +01:00
Rob Bradford
aa98589bb4 vm-migration: Add protocol documentation and data structures
Add the documentation and basic implementation for supporting migration.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-11 11:07:24 +01:00
dependabot-preview[bot]
ca5f1d6de8 build(deps): bump once_cell from 1.4.1 to 1.5.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.4.1 to 1.5.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.4.1...v1.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-11 08:47:02 +00:00
Julio Montes
270631922d vmm: openapi: remove omitempty json tag
Due to a known limitation in OpenAPITools/openapi-generator tool,
it's impossible to send go zero types, like false and 0 to
cloud-hypervisor because `omitempty` is added if a field is not
required.
Set cache_size, dax, num_queues and queue_size as required to remove
`omitempty` from the json tag.

fixes #1961

Signed-off-by: Julio Montes <julio.montes@intel.com>
2020-11-10 19:09:17 +01:00
Muminul Islam
465ef3f7ad scripts: dev_cli: Export volumes for windows test as well
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-11-09 14:58:26 -08:00
dependabot-preview[bot]
50e0423304 build(deps): bump futures from 0.3.7 to 0.3.8
Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.7 to 0.3.8.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.7...0.3.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-09 21:50:00 +00:00
Anatol Belski
906a1eb18d doc: Add notes to compile OVMF with legacy support
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
2020-11-09 17:41:03 +01:00
Rob Bradford
7b77f1ef90 vmm: Remove self-spawning functionality for vhost-user-{net,block}
This also removes the need to lookup up the "exe" symlink for finding
the VMM executable path.

Fixes: #1925

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-09 00:16:15 +01:00
Rob Bradford
0005d11e32 vmm: config: Require a socket when using vhost-user
With self-spawning being removed both parameters are now required.

Fixes: #1925

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-09 00:16:15 +01:00
Rob Bradford
ec84abc5c7 tests: Remove self spawning integration tests
Fixes: #1925

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-11-09 00:16:15 +01:00
dependabot-preview[bot]
2ea0d8eb91 build(deps): bump cc from 1.0.61 to 1.0.62
Bumps [cc](https://github.com/alexcrichton/cc-rs) from 1.0.61 to 1.0.62.
- [Release notes](https://github.com/alexcrichton/cc-rs/releases)
- [Commits](https://github.com/alexcrichton/cc-rs/compare/1.0.61...1.0.62)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-06 22:50:38 +00:00
dependabot-preview[bot]
6a25aaf6b9 build(deps): bump url from 2.1.1 to 2.2.0
Bumps [url](https://github.com/servo/rust-url) from 2.1.1 to 2.2.0.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.1.1...v2.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-06 09:06:10 +00:00
dependabot-preview[bot]
1b6b45bf2f build(deps): bump thiserror from 1.0.21 to 1.0.22
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.21 to 1.0.22.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.21...1.0.22)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-04 16:24:46 +00:00
dependabot-preview[bot]
ecda97f759 build(deps): bump micro_http from 40309b7 to 59ab644
Bumps [micro_http](https://github.com/firecracker-microvm/micro-http) from `40309b7` to `59ab644`.
- [Release notes](https://github.com/firecracker-microvm/micro-http/releases)
- [Commits](40309b72ee...59ab64440a)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-04 16:01:50 +00:00
Michael Zhao
5566992422 scripts: Fix the failure to fetch latest guest kernel code on AArch64
The integration script failed to obtain the new guest kernel commit when
the code folder had been existing on CI machine.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-11-04 16:00:12 +00:00
dependabot-preview[bot]
cc637f12f6 build(deps): bump aho-corasick from 0.7.14 to 0.7.15
Bumps [aho-corasick](https://github.com/BurntSushi/aho-corasick) from 0.7.14 to 0.7.15.
- [Release notes](https://github.com/BurntSushi/aho-corasick/releases)
- [Commits](https://github.com/BurntSushi/aho-corasick/compare/0.7.14...0.7.15)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-04 15:15:55 +00:00
dependabot-preview[bot]
592babaadd build(deps): bump ppv-lite86 from 0.2.9 to 0.2.10
Bumps [ppv-lite86](https://github.com/cryptocorrosion/cryptocorrosion) from 0.2.9 to 0.2.10.
- [Release notes](https://github.com/cryptocorrosion/cryptocorrosion/releases)
- [Commits](https://github.com/cryptocorrosion/cryptocorrosion/compare/ppv-lite86-0.2.9...ppv-lite86-0.2.10)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-03 08:31:08 +00:00
dependabot-preview[bot]
138a8156c8 build(deps): bump blake2b_simd from 0.5.10 to 0.5.11
Bumps [blake2b_simd](https://github.com/oconnor663/blake2_simd) from 0.5.10 to 0.5.11.
- [Release notes](https://github.com/oconnor663/blake2_simd/releases)
- [Commits](https://github.com/oconnor663/blake2_simd/compare/0.5.10...0.5.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-03 08:30:59 +00:00
dependabot-preview[bot]
7a008e66a4 build(deps): bump anyhow from 1.0.33 to 1.0.34
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.33 to 1.0.34.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.33...1.0.34)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-03 08:30:50 +00:00
Michael Zhao
164d481683 tests: Enable watchdog test case on AArch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-11-02 08:19:07 +00:00
Michael Zhao
52f60188ca resources: Enable watchdog in guest driver
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-11-02 08:19:07 +00:00
dependabot-preview[bot]
8cb29af712 build(deps): bump regex from 1.4.1 to 1.4.2
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.4.1...1.4.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-02 08:17:11 +00:00
dependabot-preview[bot]
60768a84c5 build(deps): bump signal-hook-registry from 1.2.1 to 1.2.2
Bumps [signal-hook-registry](https://github.com/vorner/signal-hook) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-01 09:24:50 +00:00
dependabot-preview[bot]
3266eb7cce build(deps): bump backtrace from 0.3.53 to 0.3.54
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.53 to 0.3.54.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.53...0.3.54)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-31 07:42:56 +00:00
Muminul Islam
c9a28312a9 rpm: Fix vhost_user_block binary name and use latest tag
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-10-31 07:42:39 +00:00
Michael Zhao
a278704ae0 tests: Enable reboot test case on AArch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-10-30 17:14:44 +00:00
Michael Zhao
093a581ee1 vmm: Implement VM rebooting on AArch64
The logic to handle AArch64 system event was: SHUTDOWN and RESET were
all treated as RESET.

Now we handle them differently:
- RESET event will trigger Vmm::vm_reboot(),
- SHUTDOWN event will trigger Vmm::vm_shutdown().

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-10-30 17:14:44 +00:00
Michael Zhao
69394c9c35 vmm: Handle hypervisor VCPU run result from Vcpu to VcpuManager
Now Vcpu::run() returns a boolean value to VcpuManager, indicating
whether the VM is going to reboot (false) or just continue (true).
Moving the handling of hypervisor VCPU run result from Vcpu to
VcpuManager gives us the flexibility to handle more scenarios like
shutting down on AArch64.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-10-30 17:14:44 +00:00
Michael Zhao
0f5e5d9e6d tests: Fix some build warnings in integration test on AArch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-10-30 17:14:12 +00:00
Michael Zhao
4606f0f28a tests: Make migration test case X86 only
Set the test case test_snapshot_restore X86 only, instead of excluding
it from test command line.

The command line option was added because we used to support migration
with Virtio-MMIO, but not Virtio-PCI.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-10-30 17:14:12 +00:00
Rob Bradford
cb88ceeae8 vmm: memory_manager: Move the restoration of guest memory later
Rather than filling the guest memory from a file at the point of the the
guest memory region being created instead fill from the file later. This
simplifies the region creation code but also adds flexibility for
sourcing the guest memory from a source other than an on disk file.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-10-30 12:31:47 +01:00
203 changed files with 18168 additions and 14577 deletions

View File

@@ -7,6 +7,7 @@ jobs:
name: Build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
rust:
- stable

View File

@@ -1,32 +0,0 @@
name: Cloud Hypervisor Cross Build
on: [pull_request, create]
jobs:
build:
if: github.event_name == 'pull_request'
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.46.0
target:
- aarch64-unknown-linux-gnu
- aarch64-unknown-linux-musl
steps:
- name: Code checkout
uses: actions/checkout@v2
- name: Install Rust toolchain (${{ matrix.rust }})
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Install arm64 libfdt
run: wget http://ftp.us.debian.org/debian/pool/main/d/device-tree-compiler/libfdt-dev_1.6.0-1_arm64.deb && dpkg-deb -xv libfdt-dev_1.6.0-1_arm64.deb ./tlibfdtdev && sudo mkdir /tmmmp && mkdir target && mkdir target/debug && mkdir target/debug/deps && sudo cp ./tlibfdtdev/usr/lib/aarch64-linux-gnu/libfdt.a target/debug/deps/libfdt.a && echo "libfdt installed"
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: true
command: build
args: --target=${{ matrix.target }} --no-default-features --features "kvm"

View File

@@ -23,7 +23,7 @@ jobs:
override: true
components: rustfmt, clippy
- name: Install arm64 libfdt
run: wget http://ftp.us.debian.org/debian/pool/main/d/device-tree-compiler/libfdt-dev_1.6.0-1_arm64.deb && dpkg-deb -xv libfdt-dev_1.6.0-1_arm64.deb ./tlibfdtdev && sudo mkdir /tmmmp && mkdir target && mkdir target/debug && mkdir target/debug/deps && sudo cp ./tlibfdtdev/usr/lib/aarch64-linux-gnu/libfdt.a target/debug/deps/libfdt.a && echo "libfdt installed"
run: wget http://ftp.us.debian.org/debian/pool/main/d/device-tree-compiler/libfdt-dev_1.6.0-1_arm64.deb && dpkg-deb -xv libfdt-dev_1.6.0-1_arm64.deb ./tlibfdtdev && mkdir -p target/debug/deps && sudo cp ./tlibfdtdev/usr/lib/aarch64-linux-gnu/libfdt.a target/debug/deps/libfdt.a && echo "libfdt installed"
- name: Formatting (rustfmt)
run: cargo fmt -- --check
- name: Clippy (kvm)

View File

@@ -18,19 +18,34 @@ jobs:
- name: Install Rust toolchain (${{ matrix.rust }})
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
components: rustfmt, clippy
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
components: rustfmt, clippy
- name: Formatting (rustfmt)
run: cargo fmt -- --check
- name: Clippy (all features)
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Clippy (all features,kvm)
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,kvm" -- -D warnings
- name: Clippy (all features,mshv)
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
- name: Clippy (acpi,kvm)
run: cargo clippy --all-targets --no-default-features --features "acpi,kvm" -- -D warnings
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,kvm" -- -D warnings
- name: Clippy (acpi,kvm,tdx)
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,kvm,tdx" -- -D warnings
- name: Clippy (kvm)
run: cargo clippy --all-targets --no-default-features --features "kvm" -- -D warnings
run: cargo clippy --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
- name: Clippy (acpi,mshv)
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,mshv" -- -D warnings
- name: Clippy (mshv)
run: cargo clippy --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings
- name: Clippy (integration tests)
run: cargo clippy --all --all-targets --tests --features "integration_tests" -- -D warnings

766
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "cloud-hypervisor"
version = "0.11.0"
version = "0.14.1"
authors = ["The Cloud Hypervisor Authors"]
edition = "2018"
default-run = "cloud-hypervisor"
@@ -13,53 +13,50 @@ homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
lto = true
[dependencies]
anyhow = "1.0"
anyhow = "1.0.39"
api_client = { path = "api_client" }
clap = { version = "2.33.3", features = ["wrap_help"] }
epoll = ">=4.0.1"
epoll = "4.3.1"
event_monitor = { path = "event_monitor" }
hypervisor = { path = "hypervisor" }
libc = "0.2.80"
log = { version = "0.4.11", features = ["std"] }
libc = "0.2.91"
log = { version = "0.4.14", features = ["std"] }
option_parser = { path = "option_parser" }
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.22.0" }
serde_json = "1.0.59"
thiserror = "1.0"
vhost_user_block = { path = "vhost_user_block"}
vhost_user_net = { path = "vhost_user_net"}
serde_json = "1.0.64"
signal-hook = "0.3.7"
thiserror = "1.0.24"
vmm = { path = "vmm" }
vmm-sys-util = "0.6.1"
wait-timeout = "0.2.0"
vmm-sys-util = "0.8.0"
vm-memory = "0.5.0"
[build-dependencies]
clap = { version = "2.33.3", features = ["wrap_help"] }
# List of patched crates
[patch.crates-io]
vm-memory = { git = "https://github.com/cloud-hypervisor/vm-memory", branch = "ch" }
[patch.'https://github.com/rust-vmm/vhost']
vhost_rs = { git = "https://github.com/cloud-hypervisor/vhost", branch = "ch", package = "vhost", features = ["vhost-user-master", "vhost-user-slave"] }
[patch.'https://github.com/rust-vmm/kvm-ioctls']
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
[patch.'https://github.com/rust-vmm/kvm-bindings']
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch", features = ["with-serde", "fam-wrappers"] }
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.4.0", features = ["with-serde", "fam-wrappers"] }
[dev-dependencies]
ssh2 = "0.8.2"
dirs = "3.0.1"
credibility = "0.1.3"
tempdir = "0.3.7"
dirs = "3.0.1"
lazy_static= "1.4.0"
tempfile = "3.1.0"
serde_json = "1.0.59"
net_util = { path = "net_util" }
serde_json = "1.0.64"
test_infra = { path = "test_infra" }
wait-timeout = "0.2.0"
[features]
default = ["acpi", "cmos", "io_uring", "kvm"]
# Common features for all hypervisors
common = ["acpi", "cmos", "fwdebug", "io_uring"]
acpi = ["vmm/acpi"]
cmos = ["vmm/cmos"]
fwdebug = ["vmm/fwdebug"]
kvm = ["vmm/kvm"]
mshv = ["vmm/mshv"]
io_uring = ["vmm/io_uring"]
tdx = ["vmm/tdx"]
# Integration tests require a special environment to run in
integration_tests = []
@@ -72,6 +69,7 @@ members = [
"arch_gen",
"block_util",
"devices",
"event_monitor",
"hypervisor",
"net_gen",
"net_util",
@@ -80,7 +78,6 @@ members = [
"qcow",
"vhost_user_backend",
"vhost_user_block",
"vhost_user_fs",
"vhost_user_net",
"virtio-devices",
"vmm",
@@ -89,3 +86,4 @@ members = [
"vm-migration",
"vm-virtio"
]
exclude = ["test_infra"]

74
Jenkinsfile vendored
View File

@@ -22,13 +22,9 @@ pipeline{
}
}
stage ('Build') {
failFast true
parallel {
parallel {
stage ('Worker build') {
agent { node { label 'groovy' } }
options {
timeout(time: 1, unit: 'HOURS')
}
stages {
stage ('Checkout') {
steps {
@@ -46,6 +42,9 @@ pipeline{
}
}
stage ('Run integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration"
}
@@ -54,9 +53,6 @@ pipeline{
}
stage ('AArch64 worker build') {
agent { node { label 'bionic-arm64' } }
options {
timeout(time: 1, unit: 'HOURS')
}
stages {
stage ('Checkout') {
steps {
@@ -69,6 +65,9 @@ pipeline{
}
}
stage ('Run integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration"
}
@@ -83,9 +82,6 @@ pipeline{
}
stage ('Worker build (musl)') {
agent { node { label 'groovy' } }
options {
timeout(time: 1, unit: 'HOURS')
}
stages {
stage ('Checkout') {
steps {
@@ -98,6 +94,9 @@ pipeline{
}
}
stage ('Run integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration --libc musl"
}
@@ -106,9 +105,6 @@ pipeline{
}
stage ('Worker build SGX') {
agent { node { label 'bionic-sgx' } }
options {
timeout(time: 1, unit: 'HOURS')
}
when { branch 'master' }
stages {
stage ('Checkout') {
@@ -117,11 +113,17 @@ pipeline{
}
}
stage ('Run SGX integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration-sgx"
}
}
stage ('Run SGX integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration-sgx --libc musl"
}
@@ -134,11 +136,41 @@ pipeline{
}
}
}
stage ('Worker build VFIO') {
agent { node { label 'bionic-vfio' } }
when { branch 'master' }
stages {
stage ('Checkout') {
steps {
checkout scm
}
}
stage ('Run VFIO integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration-vfio"
}
}
stage ('Run VFIO integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration-vfio --libc musl"
}
}
}
post {
always {
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
deleteDir()
}
}
}
stage ('Worker build - Windows guest') {
agent { node { label 'groovy-win' } }
options {
timeout(time: 1, unit: 'HOURS')
}
stages {
stage ('Checkout') {
steps {
@@ -150,7 +182,7 @@ pipeline{
sh "mkdir ${env.HOME}/workloads"
azureDownload(storageCredentialId: 'ch-image-store',
containerName: 'private-images',
includeFilesPattern: 'OVMF.fd',
includeFilesPattern: 'OVMF-4b47d0c6c8.fd',
downloadType: 'container',
downloadDirLoc: "${env.HOME}/workloads")
azureDownload(storageCredentialId: 'ch-image-store',
@@ -161,11 +193,17 @@ pipeline{
}
}
stage ('Run Windows guest integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration-windows"
}
}
stage ('Run Windows guest integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
}

View File

@@ -148,7 +148,7 @@ We need to get the latest `rust-hypervisor-firmware` release and also a working
$ pushd $CLOUDH
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.2.8/hypervisor-fw
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.3.0/hypervisor-fw
$ popd
```

View File

@@ -5,5 +5,5 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2018"
[dependencies]
vm-memory = "0.3.0"
vm-memory = "0.5.0"

View File

@@ -95,8 +95,7 @@ pub type Byte = u8;
impl Aml for Byte {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x0a); /* BytePrefix */
let mut bytes = vec![0x0a]; /* BytePrefix */
bytes.push(*self);
bytes
}
@@ -106,8 +105,7 @@ pub type Word = u16;
impl Aml for Word {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x0bu8); /* WordPrefix */
let mut bytes = vec![0x0bu8]; /* WordPrefix */
bytes.append(&mut self.to_le_bytes().to_vec());
bytes
}
@@ -117,8 +115,7 @@ pub type DWord = u32;
impl Aml for DWord {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x0c); /* DWordPrefix */
let mut bytes = vec![0x0c]; /* DWordPrefix */
bytes.append(&mut self.to_le_bytes().to_vec());
bytes
}
@@ -128,8 +125,7 @@ pub type QWord = u64;
impl Aml for QWord {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x0e); /* QWordPrefix */
let mut bytes = vec![0x0e]; /* QWordPrefix */
bytes.append(&mut self.to_le_bytes().to_vec());
bytes
}
@@ -147,8 +143,7 @@ impl Aml for Name {
impl Name {
pub fn new(path: Path, inner: &dyn Aml) -> Self {
let mut bytes = Vec::new();
bytes.push(0x08); /* NameOp */
let mut bytes = vec![0x08]; /* NameOp */
bytes.append(&mut path.to_aml_bytes());
bytes.append(&mut inner.to_aml_bytes());
Name { bytes }
@@ -161,8 +156,7 @@ pub struct Package<'a> {
impl<'a> Aml for Package<'a> {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(self.children.len() as u8);
let mut bytes = vec![self.children.len() as u8];
for child in &self.children {
bytes.append(&mut child.to_aml_bytes());
}
@@ -239,11 +233,11 @@ fn create_pkg_length(data: &[u8], include_self: bool) -> Vec<u8> {
result
}
pub struct EISAName {
pub struct EisaName {
value: DWord,
}
impl EISAName {
impl EisaName {
pub fn new(name: &str) -> Self {
assert_eq!(name.len(), 7);
@@ -258,11 +252,11 @@ impl EISAName {
| name.chars().nth(6).unwrap().to_digit(16).unwrap())
.swap_bytes();
EISAName { value }
EisaName { value }
}
}
impl Aml for EISAName {
impl Aml for EisaName {
fn to_aml_bytes(&self) -> Vec<u8> {
self.value.to_aml_bytes()
}
@@ -289,8 +283,7 @@ impl Aml for Usize {
}
fn create_aml_string(v: &str) -> Vec<u8> {
let mut data = Vec::new();
data.push(0x0D); /* String Op */
let mut data = vec![0x0D]; /* String Op */
data.extend_from_slice(v.as_bytes());
data.push(0x0); /* NullChar */
data
@@ -374,9 +367,7 @@ impl Memory32Fixed {
impl Aml for Memory32Fixed {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x86); /* Memory32Fixed */
let mut bytes = vec![0x86]; /* Memory32Fixed */
bytes.append(&mut 9u16.to_le_bytes().to_vec());
// 9 bytes of payload
@@ -390,7 +381,7 @@ impl Aml for Memory32Fixed {
#[derive(Copy, Clone)]
enum AddressSpaceType {
Memory,
IO,
Io,
BusNumber,
}
@@ -421,7 +412,7 @@ impl<T> AddressSpace<T> {
pub fn new_io(min: T, max: T) -> Self {
AddressSpace {
r#type: AddressSpaceType::IO,
r#type: AddressSpaceType::Io,
min,
max,
type_flags: 3, /* EntireRange */
@@ -510,16 +501,16 @@ impl Aml for AddressSpace<u64> {
}
}
pub struct IO {
pub struct Io {
min: u16,
max: u16,
alignment: u8,
length: u8,
}
impl IO {
impl Io {
pub fn new(min: u16, max: u16, alignment: u8, length: u8) -> Self {
IO {
Io {
min,
max,
alignment,
@@ -528,11 +519,10 @@ impl IO {
}
}
impl Aml for IO {
impl Aml for Io {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
let mut bytes = vec![0x47]; /* Io Port Descriptor */
bytes.push(0x47); /* IO Port Descriptor */
bytes.push(1); /* IODecode16 */
bytes.append(&mut self.min.to_le_bytes().to_vec());
bytes.append(&mut self.max.to_le_bytes().to_vec());
@@ -571,9 +561,7 @@ impl Interrupt {
impl Aml for Interrupt {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x89); /* Extended IRQ Descriptor */
let mut bytes = vec![0x89]; /* Extended IRQ Descriptor */
bytes.append(&mut 6u16.to_le_bytes().to_vec());
let flags = (self.shared as u8) << 3
| (self.active_low as u8) << 2
@@ -699,8 +687,7 @@ impl<'a> Return<'a> {
impl<'a> Aml for Return<'a> {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xa4); /* ReturnOp */
let mut bytes = vec![0xa4]; /* ReturnOp */
bytes.append(&mut self.value.to_aml_bytes());
bytes
}
@@ -788,14 +775,14 @@ impl Aml for Field {
#[derive(Clone, Copy)]
pub enum OpRegionSpace {
SystemMemory,
SystemIO,
PCIConfig,
SystemIo,
PConfig,
EmbeddedControl,
SMBus,
SystemCMOS,
Smbus,
SystemCmos,
PciBarTarget,
IPMI,
GeneralPurposeIO,
Ipmi,
GeneralPurposeIo,
GenericSerialBus,
}
@@ -876,8 +863,7 @@ impl<'a> Equal<'a> {
impl<'a> Aml for Equal<'a> {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x93); /* LEqualOp */
let mut bytes = vec![0x93]; /* LEqualOp */
bytes.extend_from_slice(&self.left.to_aml_bytes());
bytes.extend_from_slice(&self.right.to_aml_bytes());
bytes
@@ -897,8 +883,7 @@ impl<'a> LessThan<'a> {
impl<'a> Aml for LessThan<'a> {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x95); /* LLessOp */
let mut bytes = vec![0x95]; /* LLessOp */
bytes.extend_from_slice(&self.left.to_aml_bytes());
bytes.extend_from_slice(&self.right.to_aml_bytes());
bytes
@@ -940,8 +925,7 @@ impl<'a> Store<'a> {
impl<'a> Aml for Store<'a> {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x70); /* StoreOp */
let mut bytes = vec![0x70]; /* StoreOp */
bytes.extend_from_slice(&self.value.to_aml_bytes());
bytes.extend_from_slice(&self.name.to_aml_bytes());
bytes
@@ -961,8 +945,7 @@ impl Mutex {
impl Aml for Mutex {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x5b); /* ExtOpPrefix */
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
bytes.push(0x01); /* MutexOp */
bytes.extend_from_slice(&self.path.to_aml_bytes());
bytes.push(self.sync_level);
@@ -983,8 +966,7 @@ impl Acquire {
impl Aml for Acquire {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x5b); /* ExtOpPrefix */
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
bytes.push(0x23); /* AcquireOp */
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
bytes.extend_from_slice(&self.timeout.to_le_bytes());
@@ -1004,8 +986,7 @@ impl Release {
impl Aml for Release {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x5b); /* ExtOpPrefix */
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
bytes.push(0x27); /* ReleaseOp */
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
bytes
@@ -1025,8 +1006,7 @@ impl<'a> Notify<'a> {
impl<'a> Aml for Notify<'a> {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x86); /* NotifyOp */
let mut bytes = vec![0x86]; /* NotifyOp */
bytes.extend_from_slice(&self.object.to_aml_bytes());
bytes.extend_from_slice(&self.value.to_aml_bytes());
bytes
@@ -1082,8 +1062,7 @@ macro_rules! binary_op {
impl<'a> Aml for $name<'a> {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push($opcode); /* Op for the binary operator */
let mut bytes = vec![$opcode]; /* Op for the binary operator */
bytes.extend_from_slice(&self.a.to_aml_bytes());
bytes.extend_from_slice(&self.b.to_aml_bytes());
bytes.extend_from_slice(&self.target.to_aml_bytes());
@@ -1180,8 +1159,7 @@ impl<'a, T> CreateField<'a, T> {
impl<'a> Aml for CreateField<'a, u64> {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x8f); /* CreateQWordFieldOp */
let mut bytes = vec![0x8f]; /* CreateQWordFieldOp */
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
bytes.extend_from_slice(&self.offset.to_aml_bytes());
bytes.extend_from_slice(&self.field.to_aml_bytes());
@@ -1191,8 +1169,7 @@ impl<'a> Aml for CreateField<'a, u64> {
impl<'a> Aml for CreateField<'a, u32> {
fn to_aml_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0x8a); /* CreateDWordFieldOp */
let mut bytes = vec![0x8a]; /* CreateDWordFieldOp */
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
bytes.extend_from_slice(&self.offset.to_aml_bytes());
bytes.extend_from_slice(&self.field.to_aml_bytes());
@@ -1235,12 +1212,12 @@ mod tests {
Device::new(
"_SB_.COM1".into(),
vec![
&Name::new("_HID".into(), &EISAName::new("PNP0501")),
&Name::new("_HID".into(), &EisaName::new("PNP0501")),
&Name::new(
"_CRS".into(),
&ResourceTemplate::new(vec![
&Interrupt::new(true, true, false, false, 4),
&IO::new(0x3f8, 0x3f8, 0, 0x8)
&Io::new(0x3f8, 0x3f8, 0, 0x8)
])
)
]
@@ -1476,7 +1453,7 @@ mod tests {
"_CRS".into(),
&ResourceTemplate::new(vec![
&Interrupt::new(true, true, false, false, 4),
&IO::new(0x3f8, 0x3f8, 0, 0x8)
&Io::new(0x3f8, 0x3f8, 0, 0x8)
])
)
.to_aml_bytes(),
@@ -1519,7 +1496,7 @@ mod tests {
#[test]
fn test_eisa_name() {
assert_eq!(
Name::new("_HID".into(), &EISAName::new("PNP0501")).to_aml_bytes(),
Name::new("_HID".into(), &EisaName::new("PNP0501")).to_aml_bytes(),
[0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01],
)
}
@@ -1665,14 +1642,14 @@ mod tests {
#[test]
fn test_op_region() {
/*
OperationRegion (PRST, SystemIO, 0x0CD8, 0x0C)
OperationRegion (PRST, SystemIo, 0x0CD8, 0x0C)
*/
let op_region_data = [
0x5Bu8, 0x80, 0x50, 0x52, 0x53, 0x54, 0x01, 0x0B, 0xD8, 0x0C, 0x0A, 0x0C,
];
assert_eq!(
OpRegion::new("PRST".into(), OpRegionSpace::SystemIO, 0xcd8, 0xc).to_aml_bytes(),
OpRegion::new("PRST".into(), OpRegionSpace::SystemIo, 0xcd8, 0xc).to_aml_bytes(),
&op_region_data[..]
);
}
@@ -1766,7 +1743,7 @@ mod tests {
Device::new(
"_SB_.MHPC".into(),
vec![
&Name::new("_HID".into(), &EISAName::new("PNP0A06")),
&Name::new("_HID".into(), &EisaName::new("PNP0A06")),
&mutex,
&Method::new(
"TEST".into(),
@@ -1807,7 +1784,7 @@ mod tests {
Device::new(
"_SB_.MHPC".into(),
vec![
&Name::new("_HID".into(), &EISAName::new("PNP0A06")),
&Name::new("_HID".into(), &EisaName::new("PNP0A06")),
&Method::new(
"TEST".into(),
0,
@@ -1848,7 +1825,7 @@ mod tests {
Device::new(
"_SB_.MHPC".into(),
vec![
&Name::new("_HID".into(), &EISAName::new("PNP0A06")),
&Name::new("_HID".into(), &EisaName::new("PNP0A06")),
&Method::new(
"TEST".into(),
0,

View File

@@ -7,7 +7,7 @@ use vm_memory::ByteValued;
#[repr(packed)]
#[derive(Clone, Copy, Default)]
pub struct RSDP {
pub struct Rsdp {
pub signature: [u8; 8],
pub checksum: u8,
pub oem_id: [u8; 6],
@@ -19,17 +19,17 @@ pub struct RSDP {
_reserved: [u8; 3],
}
unsafe impl ByteValued for RSDP {}
unsafe impl ByteValued for Rsdp {}
impl RSDP {
impl Rsdp {
pub fn new(oem_id: [u8; 6], xsdt_addr: u64) -> Self {
let mut rsdp = RSDP {
let mut rsdp = Rsdp {
signature: *b"RSD PTR ",
checksum: 0,
oem_id,
revision: 2,
_rsdt_addr: 0,
length: std::mem::size_of::<RSDP>() as u32,
length: std::mem::size_of::<Rsdp>() as u32,
xsdt_addr,
extended_checksum: 0,
_reserved: [0; 3],
@@ -41,18 +41,18 @@ impl RSDP {
}
pub fn len() -> usize {
std::mem::size_of::<RSDP>()
std::mem::size_of::<Rsdp>()
}
}
#[cfg(test)]
mod tests {
use super::RSDP;
use super::Rsdp;
use vm_memory::bytes::ByteValued;
#[test]
fn test_rsdp() {
let rsdp = RSDP::new(*b"CHYPER", 0xdead_beef);
let rsdp = Rsdp::new(*b"CHYPER", 0xdead_beef);
let sum = rsdp
.as_slice()
.iter()

View File

@@ -24,12 +24,12 @@ impl GenericAddress {
}
}
pub struct SDT {
pub struct Sdt {
data: Vec<u8>,
}
#[allow(clippy::len_without_is_empty)]
impl SDT {
impl Sdt {
pub fn new(
signature: [u8; 4],
length: u32,
@@ -53,7 +53,7 @@ impl SDT {
assert_eq!(data.len(), 36);
data.resize(length as usize, 0);
let mut sdt = SDT { data };
let mut sdt = Sdt { data };
sdt.update_checksum();
sdt
@@ -117,11 +117,11 @@ impl SDT {
#[cfg(test)]
mod tests {
use super::SDT;
use super::Sdt;
#[test]
fn test_sdt() {
let mut sdt = SDT::new(*b"TEST", 40, 1, *b"CLOUDH", *b"TESTTEST", 1);
let mut sdt = Sdt::new(*b"TEST", 40, 1, *b"CLOUDH", *b"TESTTEST", 1);
let sum: u8 = sdt
.as_slice()
.iter()

View File

@@ -37,7 +37,7 @@ impl fmt::Display for Error {
#[derive(Clone, Copy, Debug)]
pub enum StatusCode {
Continue,
OK,
Ok,
NoContent,
BadRequest,
NotFound,
@@ -50,7 +50,7 @@ impl StatusCode {
fn from_raw(code: usize) -> StatusCode {
match code {
100 => StatusCode::Continue,
200 => StatusCode::OK,
200 => StatusCode::Ok,
204 => StatusCode::NoContent,
400 => StatusCode::BadRequest,
404 => StatusCode::NotFound,
@@ -69,7 +69,7 @@ impl StatusCode {
fn is_server_error(self) -> bool {
!matches!(
self,
StatusCode::OK | StatusCode::Continue | StatusCode::NoContent
StatusCode::Ok | StatusCode::Continue | StatusCode::NoContent
)
}
}

View File

@@ -6,24 +6,21 @@ authors = ["The Chromium OS Authors"]
[features]
default = []
acpi = ["acpi_tables"]
tdx = []
[dependencies]
acpi_tables = { path = "../acpi_tables", optional = true }
anyhow = "1.0"
byteorder = "1.3.4"
arch_gen = { path = "../arch_gen" }
byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" }
libc = "0.2.80"
log = "0.4.11"
libc = "0.2.91"
linux-loader = { version = "0.3.0", features = ["elf", "bzimage", "pe"] }
log = "0.4.14"
serde = {version = ">=1.0.27", features = ["rc"] }
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"
vm-memory = { version = "0.3.0", features = ["backend-mmap"] }
thiserror = "1.0"
vm-memory = { version = "0.5.0", features = ["backend-mmap"] }
vm-migration = { path = "../vm-migration" }
acpi_tables = { path = "../acpi_tables", optional = true }
arch_gen = { path = "../arch_gen" }
[dependencies.linux-loader]
git = "https://github.com/rust-vmm/linux-loader"
features = ["elf", "bzimage"]
[dev-dependencies]
rand = "0.7.3"

View File

@@ -16,12 +16,12 @@ use std::{io, result};
use super::super::DeviceType;
use super::super::InitramfsConfig;
use super::get_fdt_addr;
use super::gic::GICDevice;
use super::gic::GicDevice;
use super::layout::{
FDT_MAX_SIZE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, PCI_MMCONFIG_SIZE,
PCI_MMCONFIG_START,
};
use crate::aarch64::fdt::Error::CstringFDTTransform;
use crate::aarch64::fdt::Error::CstringFdtTransform;
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap};
// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
@@ -30,6 +30,8 @@ const GIC_PHANDLE: u32 = 1;
const MSI_PHANDLE: u32 = 2;
// This is a value for uniquely identifying the FDT node containing the clock definition.
const CLOCK_PHANDLE: u32 = 3;
// This is a value for uniquely identifying the FDT node containing the gpio controller.
const GPIO_PHANDLE: u32 = 4;
// Read the documentation specified when appending the root node to the FDT.
const ADDRESS_CELLS: u32 = 0x2;
@@ -45,6 +47,10 @@ const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
const IRQ_TYPE_EDGE_RISING: u32 = 1;
const IRQ_TYPE_LEVEL_HI: u32 = 4;
// Keys and Buttons
// System Power Down
const KEY_POWER: u32 = 116;
// This links to libfdt which handles the creation of the binary blob
// flattened device tree (fdt) that is passed to the kernel and indicates
// the hardware configuration of the machine.
@@ -62,7 +68,7 @@ extern "C" {
}
/// Trait for devices to be added to the Flattened Device Tree.
pub trait DeviceInfoForFDT {
pub trait DeviceInfoForFdt {
/// Returns the address where this device will be loaded.
fn addr(&self) -> u64;
/// Returns the associated interrupt for this device.
@@ -75,27 +81,27 @@ pub trait DeviceInfoForFDT {
#[derive(Debug)]
pub enum Error {
/// Failed to append node to the FDT.
AppendFDTNode(io::Error),
AppendFdtNode(io::Error),
/// Failed to append a property to the FDT.
AppendFDTProperty(io::Error),
AppendFdtProperty(io::Error),
/// Syscall for creating FDT failed.
CreateFDT(io::Error),
CreateFdt(io::Error),
/// Failed to obtain a C style string.
CstringFDTTransform(NulError),
CstringFdtTransform(NulError),
/// Failure in calling syscall for terminating this FDT.
FinishFDTReserveMap(io::Error),
FinishFdtReserveMap(io::Error),
/// Failure in writing FDT in memory.
WriteFDTToMemory(GuestMemoryError),
WriteFdtToMemory(GuestMemoryError),
}
type Result<T> = result::Result<T, Error>;
/// Creates the flattened device tree for this aarch64 VM.
pub fn create_fdt<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::BuildHasher>(
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
guest_mem: &GuestMemoryMmap,
cmdline: &CStr,
vcpu_mpidr: Vec<u64>,
device_info: &HashMap<(DeviceType, String), T, S>,
gic_device: &dyn GICDevice,
gic_device: &dyn GicDevice,
initrd: &Option<InitramfsConfig>,
pci_space_address: &(u64, u64),
) -> Result<Vec<u8>> {
@@ -139,7 +145,7 @@ pub fn create_fdt<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::BuildHash
let fdt_address = GuestAddress(get_fdt_addr(&guest_mem));
guest_mem
.write_slice(fdt_final.as_slice(), fdt_address)
.map_err(Error::WriteFDTToMemory)?;
.map_err(Error::WriteFdtToMemory)?;
Ok(fdt_final)
}
@@ -149,7 +155,7 @@ fn allocate_fdt(fdt: &mut Vec<u8>) -> Result<()> {
let mut fdt_ret = unsafe { fdt_create(fdt.as_mut_ptr() as *mut c_void, FDT_MAX_SIZE as c_int) };
if fdt_ret != 0 {
return Err(Error::CreateFDT(io::Error::last_os_error()));
return Err(Error::CreateFdt(io::Error::last_os_error()));
}
// The flattened device trees created with fdt_create() contains a list of
@@ -160,7 +166,7 @@ fn allocate_fdt(fdt: &mut Vec<u8>) -> Result<()> {
// Safe since we previously allocated this array.
fdt_ret = unsafe { fdt_finish_reservemap(fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Error::FinishFDTReserveMap(io::Error::last_os_error()));
return Err(Error::FinishFdtReserveMap(io::Error::last_os_error()));
}
Ok(())
}
@@ -169,7 +175,7 @@ fn finish_fdt(from_fdt: &mut Vec<u8>, to_fdt: &mut Vec<u8>) -> Result<()> {
// Safe since we allocated `fdt_final` and previously passed in its size.
let mut fdt_ret = unsafe { fdt_finish(from_fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Error::FinishFDTReserveMap(io::Error::last_os_error()));
return Err(Error::FinishFdtReserveMap(io::Error::last_os_error()));
}
// Safe because we allocated both arrays with the correct size.
@@ -181,25 +187,25 @@ fn finish_fdt(from_fdt: &mut Vec<u8>, to_fdt: &mut Vec<u8>) -> Result<()> {
)
};
if fdt_ret != 0 {
return Err(Error::FinishFDTReserveMap(io::Error::last_os_error()));
return Err(Error::FinishFdtReserveMap(io::Error::last_os_error()));
}
// Safe since we allocated `to_fdt`.
fdt_ret = unsafe { fdt_pack(to_fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Error::FinishFDTReserveMap(io::Error::last_os_error()));
return Err(Error::FinishFdtReserveMap(io::Error::last_os_error()));
}
Ok(())
}
// Following are auxiliary functions for appending nodes to FDT.
fn append_begin_node(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
let cstr_name = CString::new(name).map_err(CstringFDTTransform)?;
let cstr_name = CString::new(name).map_err(CstringFdtTransform)?;
// Safe because we allocated fdt and converted name to a CString
let fdt_ret = unsafe { fdt_begin_node(fdt.as_mut_ptr() as *mut c_void, cstr_name.as_ptr()) };
if fdt_ret != 0 {
return Err(Error::AppendFDTNode(io::Error::last_os_error()));
return Err(Error::AppendFdtNode(io::Error::last_os_error()));
}
Ok(())
}
@@ -208,7 +214,7 @@ fn append_end_node(fdt: &mut Vec<u8>) -> Result<()> {
// Safe because we allocated fdt.
let fdt_ret = unsafe { fdt_end_node(fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Error::AppendFDTNode(io::Error::last_os_error()));
return Err(Error::AppendFdtNode(io::Error::last_os_error()));
}
Ok(())
}
@@ -223,13 +229,13 @@ fn append_property_u64(fdt: &mut Vec<u8>, name: &str, val: u64) -> Result<()> {
}
fn append_property_string(fdt: &mut Vec<u8>, name: &str, value: &str) -> Result<()> {
let cstr_value = CString::new(value).map_err(CstringFDTTransform)?;
let cstr_value = CString::new(value).map_err(CstringFdtTransform)?;
append_property_cstring(fdt, name, &cstr_value)
}
fn append_property_cstring(fdt: &mut Vec<u8>, name: &str, cstr_value: &CStr) -> Result<()> {
let value_bytes = cstr_value.to_bytes_with_nul();
let cstr_name = CString::new(name).map_err(CstringFDTTransform)?;
let cstr_name = CString::new(name).map_err(CstringFdtTransform)?;
// Safe because we allocated fdt, converted name and value to CStrings
let fdt_ret = unsafe {
fdt_property(
@@ -240,13 +246,13 @@ fn append_property_cstring(fdt: &mut Vec<u8>, name: &str, cstr_value: &CStr) ->
)
};
if fdt_ret != 0 {
return Err(Error::AppendFDTProperty(io::Error::last_os_error()));
return Err(Error::AppendFdtProperty(io::Error::last_os_error()));
}
Ok(())
}
fn append_property_null(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
let cstr_name = CString::new(name).map_err(CstringFDTTransform)?;
let cstr_name = CString::new(name).map_err(CstringFdtTransform)?;
// Safe because we allocated fdt, converted name to a CString
let fdt_ret = unsafe {
@@ -258,13 +264,13 @@ fn append_property_null(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
)
};
if fdt_ret != 0 {
return Err(Error::AppendFDTProperty(io::Error::last_os_error()));
return Err(Error::AppendFdtProperty(io::Error::last_os_error()));
}
Ok(())
}
fn append_property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<()> {
let cstr_name = CString::new(name).map_err(CstringFDTTransform)?;
let cstr_name = CString::new(name).map_err(CstringFdtTransform)?;
let val_ptr = val.as_ptr() as *const c_void;
// Safe because we allocated fdt and converted name to a CString
@@ -277,7 +283,7 @@ fn append_property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<()> {
)
};
if fdt_ret != 0 {
return Err(Error::AppendFDTProperty(io::Error::last_os_error()));
return Err(Error::AppendFdtProperty(io::Error::last_os_error()));
}
Ok(())
}
@@ -374,7 +380,7 @@ fn create_chosen_node(
Ok(())
}
fn create_gic_node(fdt: &mut Vec<u8>, gic_device: &dyn GICDevice) -> Result<()> {
fn create_gic_node(fdt: &mut Vec<u8>, gic_device: &dyn GicDevice) -> Result<()> {
let gic_reg_prop = generate_prop64(gic_device.device_properties());
append_begin_node(fdt, "intc")?;
@@ -466,7 +472,7 @@ fn create_psci_node(fdt: &mut Vec<u8>) -> Result<()> {
Ok(())
}
fn create_virtio_node<T: DeviceInfoForFDT + Clone + Debug>(
fn create_virtio_node<T: DeviceInfoForFdt + Clone + Debug>(
fdt: &mut Vec<u8>,
dev_info: &T,
) -> Result<()> {
@@ -483,15 +489,16 @@ fn create_virtio_node<T: DeviceInfoForFDT + Clone + Debug>(
Ok(())
}
fn create_serial_node<T: DeviceInfoForFDT + Clone + Debug>(
fn create_serial_node<T: DeviceInfoForFdt + Clone + Debug>(
fdt: &mut Vec<u8>,
dev_info: &T,
) -> Result<()> {
let compatible = b"arm,pl011\0arm,primecell\0";
let serial_reg_prop = generate_prop64(&[dev_info.addr(), dev_info.length()]);
let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, dev_info.irq(), IRQ_TYPE_EDGE_RISING]);
append_begin_node(fdt, &format!("uart@{:x}", dev_info.addr()))?;
append_property_string(fdt, "compatible", "ns16550a")?;
append_begin_node(fdt, &format!("pl011@{:x}", dev_info.addr()))?;
append_property(fdt, "compatible", compatible)?;
append_property(fdt, "reg", &serial_reg_prop)?;
append_property_u32(fdt, "clocks", CLOCK_PHANDLE)?;
append_property_string(fdt, "clock-names", "apb_pclk")?;
@@ -501,7 +508,7 @@ fn create_serial_node<T: DeviceInfoForFDT + Clone + Debug>(
Ok(())
}
fn create_rtc_node<T: DeviceInfoForFDT + Clone + Debug>(
fn create_rtc_node<T: DeviceInfoForFdt + Clone + Debug>(
fdt: &mut Vec<u8>,
dev_info: &T,
) -> Result<()> {
@@ -519,7 +526,42 @@ fn create_rtc_node<T: DeviceInfoForFDT + Clone + Debug>(
Ok(())
}
fn create_devices_node<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::BuildHasher>(
fn create_gpio_node<T: DeviceInfoForFdt + Clone + Debug>(
fdt: &mut Vec<u8>,
dev_info: &T,
) -> Result<()> {
// PL061 GPIO controller node
let compatible = b"arm,pl061\0arm,primecell\0";
let gpio_reg_prop = generate_prop64(&[dev_info.addr(), dev_info.length()]);
let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, dev_info.irq(), IRQ_TYPE_EDGE_RISING]);
append_begin_node(fdt, &format!("pl061@{:x}", dev_info.addr()))?;
append_property(fdt, "compatible", compatible)?;
append_property(fdt, "reg", &gpio_reg_prop)?;
append_property(fdt, "interrupts", &irq)?;
append_property_null(fdt, "gpio-controller")?;
append_property_u32(fdt, "#gpio-cells", 2)?;
append_property_u32(fdt, "clocks", CLOCK_PHANDLE)?;
append_property_string(fdt, "clock-names", "apb_pclk")?;
append_property_u32(fdt, "phandle", GPIO_PHANDLE)?;
append_end_node(fdt)?;
// gpio-keys node
append_begin_node(fdt, "/gpio-keys")?;
append_property_string(fdt, "compatible", "gpio-keys")?;
append_property_u32(fdt, "#size-cells", 0)?;
append_property_u32(fdt, "#address-cells", 1)?;
append_begin_node(fdt, "/gpio-keys/poweroff")?;
append_property_string(fdt, "label", "GPIO Key Poweroff")?;
append_property_u32(fdt, "linux,code", KEY_POWER)?;
let gpios = generate_prop32(&[GPIO_PHANDLE, 3, 0]);
append_property(fdt, "gpios", &gpios)?;
append_end_node(fdt)?;
append_end_node(fdt)?;
Ok(())
}
fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
fdt: &mut Vec<u8>,
dev_info: &HashMap<(DeviceType, String), T, S>,
) -> Result<()> {
@@ -528,7 +570,8 @@ fn create_devices_node<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::Buil
for ((device_type, _device_id), info) in dev_info {
match device_type {
DeviceType::RTC => create_rtc_node(fdt, info)?,
DeviceType::Gpio => create_gpio_node(fdt, info)?,
DeviceType::Rtc => create_rtc_node(fdt, info)?,
DeviceType::Serial => create_serial_node(fdt, info)?,
DeviceType::Virtio(_) => {
ordered_virtio_device.push(info);

View File

@@ -4,9 +4,9 @@
pub mod kvm {
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGICDevice};
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice};
use crate::aarch64::gic::redist_regs::{get_redist_regs, set_redist_regs};
use crate::aarch64::gic::GICDevice;
use crate::aarch64::gic::GicDevice;
use crate::layout;
use anyhow::anyhow;
use hypervisor::kvm::kvm_bindings;
@@ -37,14 +37,14 @@ pub mod kvm {
/// Error in restoring GIC redistributor registers.
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
/// Error in saving GIC CPU interface registers.
SaveICCRegisters(crate::aarch64::gic::Error),
SaveIccRegisters(crate::aarch64::gic::Error),
/// Error in restoring GIC CPU interface registers.
RestoreICCRegisters(crate::aarch64::gic::Error),
RestoreIccRegisters(crate::aarch64::gic::Error),
}
type Result<T> = result::Result<T, Error>;
pub struct KvmGICv3 {
pub struct KvmGicV3 {
/// The hypervisor agnostic device
device: Arc<dyn hypervisor::Device>,
@@ -67,34 +67,34 @@ pub mod kvm {
gicd_ctlr: u32,
}
impl KvmGICv3 {
impl KvmGicV3 {
// Unfortunately bindgen omits defines that are based on other defines.
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
pub const SZ_64K: u64 = 0x0001_0000;
const KVM_VGIC_V3_DIST_SIZE: u64 = KvmGICv3::SZ_64K;
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * KvmGICv3::SZ_64K);
const KVM_VGIC_V3_DIST_SIZE: u64 = KvmGicV3::SZ_64K;
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * KvmGicV3::SZ_64K);
// Device trees specific constants
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
/// Get the address of the GIC distributor.
pub fn get_dist_addr() -> u64 {
layout::MAPPED_IO_START - KvmGICv3::KVM_VGIC_V3_DIST_SIZE
layout::MAPPED_IO_START - KvmGicV3::KVM_VGIC_V3_DIST_SIZE
}
/// Get the size of the GIC distributor.
pub fn get_dist_size() -> u64 {
KvmGICv3::KVM_VGIC_V3_DIST_SIZE
KvmGicV3::KVM_VGIC_V3_DIST_SIZE
}
/// Get the address of the GIC redistributors.
pub fn get_redists_addr(vcpu_count: u64) -> u64 {
KvmGICv3::get_dist_addr() - KvmGICv3::get_redists_size(vcpu_count)
KvmGicV3::get_dist_addr() - KvmGicV3::get_redists_size(vcpu_count)
}
/// Get the size of the GIC redistributors.
pub fn get_redists_size(vcpu_count: u64) -> u64 {
vcpu_count * KvmGICv3::KVM_VGIC_V3_REDIST_SIZE
vcpu_count * KvmGicV3::KVM_VGIC_V3_REDIST_SIZE
}
/// Save the state of GIC.
@@ -112,7 +112,7 @@ pub mod kvm {
.map_err(Error::SaveRedistributorRegisters)?;
let icc_state =
get_icc_regs(&self.device(), &gicr_typers).map_err(Error::SaveICCRegisters)?;
get_icc_regs(&self.device(), &gicr_typers).map_err(Error::SaveIccRegisters)?;
Ok(Gicv3State {
dist: dist_state,
@@ -134,13 +134,13 @@ pub mod kvm {
.map_err(Error::RestoreRedistributorRegisters)?;
set_icc_regs(&self.device(), &gicr_typers, &state.icc)
.map_err(Error::RestoreICCRegisters)?;
.map_err(Error::RestoreIccRegisters)?;
Ok(())
}
}
impl GICDevice for KvmGICv3 {
impl GicDevice for KvmGicV3 {
fn device(&self) -> &Arc<dyn hypervisor::Device> {
&self.device
}
@@ -150,7 +150,7 @@ pub mod kvm {
}
fn fdt_maint_irq(&self) -> u32 {
KvmGICv3::ARCH_GIC_V3_MAINT_IRQ
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
}
fn device_properties(&self) -> &[u64] {
@@ -170,7 +170,7 @@ pub mod kvm {
}
}
impl KvmGICDevice for KvmGICv3 {
impl KvmGicDevice for KvmGicV3 {
fn version() -> u32 {
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
}
@@ -178,15 +178,15 @@ pub mod kvm {
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GICDevice> {
Box::new(KvmGICv3 {
) -> Box<dyn GicDevice> {
Box::new(KvmGicV3 {
device,
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
properties: [
KvmGICv3::get_dist_addr(),
KvmGICv3::get_dist_size(),
KvmGICv3::get_redists_addr(vcpu_count),
KvmGICv3::get_redists_size(vcpu_count),
KvmGicV3::get_dist_addr(),
KvmGicV3::get_dist_size(),
KvmGicV3::get_redists_addr(vcpu_count),
KvmGicV3::get_redists_size(vcpu_count),
],
vcpu_count,
})
@@ -194,7 +194,7 @@ pub mod kvm {
fn init_device_attributes(
_vm: &Arc<dyn hypervisor::Vm>,
gic_device: &dyn GICDevice,
gic_device: &dyn GicDevice,
) -> crate::aarch64::gic::Result<()> {
/* Setting up the distributor attribute.
We are placing the GIC below 1GB so we need to substract the size of the distributor.
@@ -203,7 +203,7 @@ pub mod kvm {
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
&KvmGICv3::get_dist_addr() as *const u64 as u64,
&KvmGicV3::get_dist_addr() as *const u64 as u64,
0,
)?;
@@ -214,7 +214,7 @@ pub mod kvm {
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
&KvmGICv3::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64,
&KvmGicV3::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64,
0,
)?;
@@ -223,7 +223,7 @@ pub mod kvm {
}
pub const GIC_V3_SNAPSHOT_ID: &str = "gic-v3";
impl Snapshottable for KvmGICv3 {
impl Snapshottable for KvmGicV3 {
fn id(&self) -> String {
GIC_V3_SNAPSHOT_ID.to_string()
}
@@ -269,7 +269,7 @@ pub mod kvm {
}
}
impl Pausable for KvmGICv3 {}
impl Transportable for KvmGICv3 {}
impl Migratable for KvmGICv3 {}
impl Pausable for KvmGicV3 {}
impl Transportable for KvmGicV3 {}
impl Migratable for KvmGicV3 {}
}

View File

@@ -7,12 +7,12 @@ pub mod kvm {
use std::sync::Arc;
use std::{boxed::Box, result};
type Result<T> = result::Result<T, Error>;
use crate::aarch64::gic::gicv3::kvm::KvmGICv3;
use crate::aarch64::gic::kvm::KvmGICDevice;
use crate::aarch64::gic::{Error, GICDevice};
use crate::aarch64::gic::gicv3::kvm::KvmGicV3;
use crate::aarch64::gic::kvm::KvmGicDevice;
use crate::aarch64::gic::{Error, GicDevice};
use hypervisor::kvm::kvm_bindings;
pub struct KvmGICv3ITS {
pub struct KvmGicV3Its {
/// The hypervisor agnostic device
device: Arc<dyn hypervisor::Device>,
@@ -29,19 +29,19 @@ pub mod kvm {
vcpu_count: u64,
}
impl KvmGICv3ITS {
const KVM_VGIC_V3_ITS_SIZE: u64 = (2 * KvmGICv3::SZ_64K);
impl KvmGicV3Its {
const KVM_VGIC_V3_ITS_SIZE: u64 = (2 * KvmGicV3::SZ_64K);
fn get_msi_size() -> u64 {
KvmGICv3ITS::KVM_VGIC_V3_ITS_SIZE
KvmGicV3Its::KVM_VGIC_V3_ITS_SIZE
}
fn get_msi_addr(vcpu_count: u64) -> u64 {
KvmGICv3::get_redists_addr(vcpu_count) - KvmGICv3ITS::get_msi_size()
KvmGicV3::get_redists_addr(vcpu_count) - KvmGicV3Its::get_msi_size()
}
}
impl GICDevice for KvmGICv3ITS {
impl GicDevice for KvmGicV3Its {
fn device(&self) -> &Arc<dyn hypervisor::Device> {
&self.device
}
@@ -59,7 +59,7 @@ pub mod kvm {
}
fn fdt_maint_irq(&self) -> u32 {
KvmGICv3::ARCH_GIC_V3_MAINT_IRQ
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
}
fn msi_properties(&self) -> &[u64] {
@@ -83,27 +83,27 @@ pub mod kvm {
}
}
impl KvmGICDevice for KvmGICv3ITS {
impl KvmGicDevice for KvmGicV3Its {
fn version() -> u32 {
KvmGICv3::version()
KvmGicV3::version()
}
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GICDevice> {
Box::new(KvmGICv3ITS {
) -> Box<dyn GicDevice> {
Box::new(KvmGicV3Its {
device,
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
gic_properties: [
KvmGICv3::get_dist_addr(),
KvmGICv3::get_dist_size(),
KvmGICv3::get_redists_addr(vcpu_count),
KvmGICv3::get_redists_size(vcpu_count),
KvmGicV3::get_dist_addr(),
KvmGicV3::get_dist_size(),
KvmGicV3::get_redists_addr(vcpu_count),
KvmGicV3::get_redists_size(vcpu_count),
],
msi_properties: [
KvmGICv3ITS::get_msi_addr(vcpu_count),
KvmGICv3ITS::get_msi_size(),
KvmGicV3Its::get_msi_addr(vcpu_count),
KvmGicV3Its::get_msi_size(),
],
vcpu_count,
})
@@ -111,9 +111,9 @@ pub mod kvm {
fn init_device_attributes(
vm: &Arc<dyn hypervisor::Vm>,
gic_device: &dyn GICDevice,
gic_device: &dyn GicDevice,
) -> Result<()> {
KvmGICv3::init_device_attributes(vm, gic_device)?;
KvmGicV3::init_device_attributes(vm, gic_device)?;
let mut its_device = kvm_bindings::kvm_create_device {
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
@@ -123,13 +123,13 @@ pub mod kvm {
let its_fd = vm
.create_device(&mut its_device)
.map_err(Error::CreateGIC)?;
.map_err(Error::CreateGic)?;
Self::set_device_attribute(
&its_fd,
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
&KvmGICv3ITS::get_msi_addr(gic_device.vcpu_count()) as *const u64 as u64,
&KvmGicV3Its::get_msi_addr(gic_device.vcpu_count()) as *const u64 as u64,
0,
)?;

View File

@@ -18,7 +18,7 @@ use std::sync::Arc;
#[derive(Debug)]
pub enum Error {
/// Error while calling KVM ioctl for setting up the global interrupt controller.
CreateGIC(hypervisor::HypervisorVmError),
CreateGic(hypervisor::HypervisorVmError),
/// Error while setting device attributes for the GIC.
SetDeviceAttribute(hypervisor::HypervisorDeviceError),
/// Error while getting device attributes for the GIC.
@@ -26,7 +26,7 @@ pub enum Error {
}
type Result<T> = result::Result<T, Error>;
pub trait GICDevice: Send {
pub trait GicDevice: Send {
/// Returns the hypervisor agnostic Device of the GIC device
fn device(&self) -> &Arc<dyn hypervisor::Device>;
@@ -65,16 +65,16 @@ pub trait GICDevice: Send {
}
pub mod kvm {
use super::GICDevice;
use super::GicDevice;
use super::Result;
use crate::aarch64::gic::gicv3_its::kvm::KvmGICv3ITS;
use crate::aarch64::gic::gicv3_its::kvm::KvmGicV3Its;
use crate::layout;
use hypervisor::kvm::kvm_bindings;
use std::boxed::Box;
use std::sync::Arc;
/// Trait for GIC devices.
pub trait KvmGICDevice: Send + Sync + GICDevice {
pub trait KvmGicDevice: Send + Sync + GicDevice {
/// Returns the GIC version of the device
fn version() -> u32;
@@ -82,12 +82,12 @@ pub mod kvm {
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GICDevice>;
) -> Box<dyn GicDevice>;
/// Setup the device-specific attributes
fn init_device_attributes(
vm: &Arc<dyn hypervisor::Vm>,
gic_device: &dyn GICDevice,
gic_device: &dyn GicDevice,
) -> Result<()>;
/// Initialize a GIC device
@@ -99,7 +99,7 @@ pub mod kvm {
};
vm.create_device(&mut gic_device)
.map_err(super::Error::CreateGIC)
.map_err(super::Error::CreateGic)
}
/// Set a GIC device attribute
@@ -145,7 +145,7 @@ pub mod kvm {
}
/// Finalize the setup of a GIC device
fn finalize_device(gic_device: &dyn GICDevice) -> Result<()> {
fn finalize_device(gic_device: &dyn GicDevice) -> Result<()> {
/* We need to tell the kernel how many irqs to support with this vgic.
* See the `layout` module for details.
*/
@@ -174,7 +174,8 @@ pub mod kvm {
}
/// Method to initialize the GIC device
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
#[allow(clippy::new_ret_no_self)]
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
let vgic_fd = Self::init_device(vm)?;
let device = Self::create_device(vgic_fd, vcpu_count);
@@ -189,9 +190,9 @@ pub mod kvm {
/// Create a GICv3-ITS device.
///
pub fn create_gic(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
pub fn create_gic(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
debug!("creating a GICv3-ITS");
KvmGICv3ITS::new(vm, vcpu_count)
KvmGicV3Its::new(vm, vcpu_count)
}
/// Function that saves RDIST pending tables into guest RAM.

View File

@@ -43,7 +43,7 @@
//
//
use vm_memory::{GuestAddress, GuestUsize};
use vm_memory::GuestAddress;
/// Below this address will reside the GIC, above this address will reside the MMIO devices.
pub const MAPPED_IO_START: u64 = 0x0900_0000;
@@ -51,6 +51,7 @@ pub const MAPPED_IO_START: u64 = 0x0900_0000;
/// Space 0x0900_0000 ~ 0x1000_0000 is reserved for legacy devices.
pub const LEGACY_SERIAL_MAPPED_IO_START: u64 = 0x0900_0000;
pub const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
pub const LEGACY_GPIO_MAPPED_IO_START: u64 = 0x0902_0000;
/// Legacy space will be allocated at once whiling setting up legacy devices.
pub const LEGACY_DEVICES_MAPPED_IO_SIZE: u64 = 0x0700_0000;
@@ -61,7 +62,7 @@ pub const MEM_32BIT_DEVICES_SIZE: u64 = 0x3000_0000;
/// PCI MMCONFIG space (start: after the device space at 1 GiB, length: 256MiB)
pub const PCI_MMCONFIG_START: GuestAddress = GuestAddress(0x4000_0000);
pub const PCI_MMCONFIG_SIZE: GuestUsize = 256 << 20;
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
/// Start of RAM on 64 bit ARM.
pub const RAM_64BIT_START: u64 = 0x8000_0000;
@@ -78,9 +79,9 @@ pub const FDT_MAX_SIZE: usize = 0x20_0000;
// * bigger than 32
// * less than 1023 and
// * a multiple of 32.
// We are setting up our interrupt controller to support a maximum of 128 interrupts.
// We are setting up our interrupt controller to support a maximum of 256 interrupts.
/// First usable interrupt on aarch64.
pub const IRQ_BASE: u32 = 32;
pub const IRQ_BASE: u32 = 0;
/// Last usable interrupt on aarch64.
pub const IRQ_MAX: u32 = 159;
pub const IRQ_MAX: u32 = 255;

View File

@@ -11,10 +11,10 @@ pub mod layout;
/// Logic for configuring aarch64 registers.
pub mod regs;
pub use self::fdt::DeviceInfoForFDT;
pub use self::fdt::DeviceInfoForFdt;
use crate::DeviceType;
use crate::RegionType;
use aarch64::gic::GICDevice;
use aarch64::gic::GicDevice;
use std::collections::HashMap;
use std::ffi::CStr;
use std::fmt::Debug;
@@ -28,19 +28,19 @@ use vm_memory::{
#[derive(Debug)]
pub enum Error {
/// Failed to create a FDT.
SetupFDT(fdt::Error),
SetupFdt(fdt::Error),
/// Failed to create a GIC.
SetupGIC(gic::Error),
SetupGic(gic::Error),
/// Failed to compute the initramfs address.
InitramfsAddress,
/// Error configuring the general purpose registers
REGSConfiguration(regs::Error),
RegsConfiguration(regs::Error),
/// Error configuring the MPIDR register
VcpuRegMPIDR(hypervisor::HypervisorCpuError),
VcpuRegMpidr(hypervisor::HypervisorCpuError),
}
impl From<Error> for super::Error {
@@ -63,7 +63,6 @@ pub fn configure_vcpu(
id: u8,
kernel_entry_point: Option<EntryPoint>,
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
_phys_bits: u8,
) -> super::Result<u64> {
if let Some(kernel_entry_point) = kernel_entry_point {
regs::setup_regs(
@@ -72,43 +71,39 @@ pub fn configure_vcpu(
kernel_entry_point.entry_addr.raw_value(),
&vm_memory.memory(),
)
.map_err(Error::REGSConfiguration)?;
.map_err(Error::RegsConfiguration)?;
}
let mpidr = fd.read_mpidr().map_err(Error::VcpuRegMPIDR)?;
let mpidr = fd.read_mpidr().map_err(Error::VcpuRegMpidr)?;
Ok(mpidr)
}
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
let mut regions = Vec::new();
// 0 ~ 256 MiB: Reserved
regions.push((
GuestAddress(0),
layout::MEM_32BIT_DEVICES_START.0 as usize,
RegionType::Reserved,
));
// 256 MiB ~ 1 G: MMIO space
regions.push((
layout::MEM_32BIT_DEVICES_START,
layout::MEM_32BIT_DEVICES_SIZE as usize,
RegionType::SubRegion,
));
// 1G ~ 2G: reserved. The leading 256M for PCIe MMCONFIG space
regions.push((
layout::PCI_MMCONFIG_START,
(layout::RAM_64BIT_START - layout::PCI_MMCONFIG_START.0) as usize,
RegionType::Reserved,
));
regions.push((
GuestAddress(layout::RAM_64BIT_START),
size as usize,
RegionType::Ram,
));
regions
vec![
// 0 ~ 256 MiB: Reserved
(
GuestAddress(0),
layout::MEM_32BIT_DEVICES_START.0 as usize,
RegionType::Reserved,
),
// 256 MiB ~ 1 G: MMIO space
(
layout::MEM_32BIT_DEVICES_START,
layout::MEM_32BIT_DEVICES_SIZE as usize,
RegionType::SubRegion,
),
// 1G ~ 2G: reserved. The leading 256M for PCIe MMCONFIG space
(
layout::PCI_MMCONFIG_START,
(layout::RAM_64BIT_START - layout::PCI_MMCONFIG_START.0) as usize,
RegionType::Reserved,
),
(
GuestAddress(layout::RAM_64BIT_START),
size as usize,
RegionType::Ram,
),
]
}
/// Configures the system and should be called once per vm before starting vcpu threads.
@@ -118,7 +113,7 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
/// * `guest_mem` - The memory to be used by the guest.
/// * `num_cpus` - Number of virtual CPUs the guest will have.
#[allow(clippy::too_many_arguments)]
pub fn configure_system<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::BuildHasher>(
pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
vm: &Arc<dyn hypervisor::Vm>,
guest_mem: &GuestMemoryMmap,
cmdline_cstring: &CStr,
@@ -127,8 +122,8 @@ pub fn configure_system<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::Bui
device_info: &HashMap<(DeviceType, String), T, S>,
initrd: &Option<super::InitramfsConfig>,
pci_space_address: &(u64, u64),
) -> super::Result<Box<dyn GICDevice>> {
let gic_device = gic::kvm::create_gic(vm, vcpu_count).map_err(Error::SetupGIC)?;
) -> super::Result<Box<dyn GicDevice>> {
let gic_device = gic::kvm::create_gic(vm, vcpu_count).map_err(Error::SetupGic)?;
fdt::create_fdt(
guest_mem,
@@ -139,7 +134,7 @@ pub fn configure_system<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::Bui
initrd,
pci_space_address,
)
.map_err(Error::SetupFDT)?;
.map_err(Error::SetupFdt)?;
Ok(gic_device)
}

View File

@@ -6,13 +6,7 @@
//! Implements platform specific functionality.
//! Supported platforms: x86_64, aarch64.
#![allow(
clippy::unreadable_literal,
clippy::redundant_static_lifetimes,
clippy::cast_lossless,
clippy::transmute_ptr_to_ptr,
clippy::cast_ptr_alignment
)]
#![allow(clippy::transmute_ptr_to_ptr, clippy::redundant_static_lifetimes)]
extern crate anyhow;
extern crate byteorder;
@@ -31,6 +25,7 @@ extern crate vm_migration;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate thiserror;
use std::fmt;
use std::result;
@@ -61,7 +56,7 @@ pub enum Error {
/// Error writing module entry to guest memory.
ModlistSetup(vm_memory::GuestMemoryError),
/// RSDP Beyond Guest Memory
RSDPPastRamEnd,
RsdpPastRamEnd,
}
/// Type for returning public functions outcome.
@@ -92,7 +87,7 @@ pub mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use aarch64::{
arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFDT,
arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFdt,
get_host_cpu_phys_bits, get_kernel_start, initramfs_load_addr, layout,
layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, EntryPoint,
};
@@ -133,7 +128,10 @@ pub enum DeviceType {
Serial,
/// Device Type: RTC.
#[cfg(target_arch = "aarch64")]
RTC,
Rtc,
/// Device Type: GPIO.
#[cfg(target_arch = "aarch64")]
Gpio,
}
/// Default (smallest) memory page size for the supported architectures.
@@ -148,13 +146,13 @@ impl fmt::Display for DeviceType {
/// Structure to describe MMIO device information
#[derive(Clone, Debug)]
#[cfg(target_arch = "aarch64")]
pub struct MMIODeviceInfo {
pub struct MmioDeviceInfo {
pub addr: u64,
pub irq: u32,
}
#[cfg(target_arch = "aarch64")]
impl DeviceInfoForFDT for MMIODeviceInfo {
impl DeviceInfoForFdt for MmioDeviceInfo {
fn addr(&self) -> u64 {
self.addr
}
@@ -162,6 +160,6 @@ impl DeviceInfoForFDT for MMIODeviceInfo {
self.irq
}
fn length(&self) -> u64 {
4096 as u64
4096
}
}

View File

@@ -5,15 +5,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use hypervisor::x86_64::LapicState;
use std::io::Cursor;
use std::mem;
use std::result;
use std::sync::Arc;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use hypervisor::x86_64::LapicState;
#[derive(Debug)]
pub enum Error {
GetLapic(anyhow::Error),
@@ -83,10 +81,6 @@ pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
#[cfg(test)]
mod tests {
extern crate rand;
use self::rand::Rng;
use super::*;
const KVM_APIC_REG_SIZE: usize = 0x400;
@@ -110,8 +104,15 @@ mod tests {
#[test]
fn test_apic_delivery_mode() {
let mut rng = rand::thread_rng();
let mut v: Vec<u32> = (0..20).map(|_| rng.gen::<u32>()).collect();
let mut v: Vec<u32> = Vec::new();
v.resize(20, 0);
unsafe {
assert_eq!(
libc::getrandom(v.as_mut_ptr() as *mut _ as *mut libc::c_void, 80, 0),
80
);
}
v.iter_mut()
.for_each(|x| *x = set_apic_delivery_mode(*x, 2));

View File

@@ -7,7 +7,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use vm_memory::{GuestAddress, GuestUsize};
use vm_memory::GuestAddress;
/*
@@ -81,22 +81,22 @@ pub const HIGH_RAM_START: GuestAddress = GuestAddress(0x100000);
// ** 32-bit reserved area (start: 3GiB, length: 1GiB) **
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xc000_0000);
pub const MEM_32BIT_RESERVED_SIZE: GuestUsize = 1024 << 20;
pub const MEM_32BIT_RESERVED_SIZE: u64 = 1024 << 20;
// == Fixed constants within the "32-bit reserved" range ==
// Sub range: 32-bit PCI devices (start: 3GiB, length: 640Mib)
pub const MEM_32BIT_DEVICES_START: GuestAddress = MEM_32BIT_RESERVED_START;
pub const MEM_32BIT_DEVICES_SIZE: GuestUsize = 640 << 20;
pub const MEM_32BIT_DEVICES_SIZE: u64 = 640 << 20;
// PCI MMCONFIG space (start: after the device space, length: 256MiB)
pub const PCI_MMCONFIG_START: GuestAddress =
GuestAddress(MEM_32BIT_DEVICES_START.0 + MEM_32BIT_DEVICES_SIZE);
pub const PCI_MMCONFIG_SIZE: GuestUsize = 256 << 20;
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
// IOAPIC
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
pub const IOAPIC_SIZE: GuestUsize = 0x20;
pub const IOAPIC_SIZE: u64 = 0x20;
// APIC
pub const APIC_START: GuestAddress = GuestAddress(0xfee0_0000);

View File

@@ -7,7 +7,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use std::sync::Arc;
mod gdt;
pub mod interrupts;
pub mod layout;
mod mptable;
@@ -26,6 +25,8 @@ use vm_memory::{
};
mod smbios;
use std::arch::x86_64;
#[cfg(feature = "tdx")]
pub mod tdx;
#[derive(Debug, Copy, Clone)]
pub enum BootProtocol {
@@ -146,16 +147,16 @@ pub enum Error {
MpTableSetup(mptable::Error),
/// Error configuring the general purpose registers
REGSConfiguration(regs::Error),
RegsConfiguration(regs::Error),
/// Error configuring the special registers
SREGSConfiguration(regs::Error),
SregsConfiguration(regs::Error),
/// Error configuring the floating point related registers
FPUConfiguration(regs::Error),
FpuConfiguration(regs::Error),
/// Error configuring the MSR registers
MSRSConfiguration(regs::Error),
MsrsConfiguration(regs::Error),
/// Failed to set supported CPUs.
SetSupportedCpusFailed(anyhow::Error),
@@ -174,9 +175,6 @@ pub enum Error {
/// Missing SGX_LC CPU feature
MissingSgxLaunchControlFeature,
// Error populating Cpuid
PopulatingCpuid,
}
impl From<Error> for super::Error {
@@ -185,7 +183,7 @@ impl From<Error> for super::Error {
}
}
#[allow(dead_code)]
#[allow(dead_code, clippy::upper_case_acronyms)]
#[derive(Copy, Clone)]
pub enum CpuidReg {
EAX,
@@ -337,87 +335,12 @@ pub fn configure_vcpu(
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
cpuid: CpuId,
kvm_hyperv: bool,
phys_bits: u8,
) -> super::Result<()> {
// Per vCPU CPUID changes; common are handled via CpuManager::generate_common_cpuid()
let mut cpuid = cpuid;
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
if kvm_hyperv {
// Remove conflicting entries
cpuid.retain(|c| c.function != 0x4000_0000);
cpuid.retain(|c| c.function != 0x4000_0001);
// See "Hypervisor Top Level Functional Specification" for details
// Compliance with "Hv#1" requires leaves up to 0x4000_000a
cpuid
.push(CpuIdEntry {
function: 0x40000000,
eax: 0x4000000a, // Maximum cpuid leaf
ebx: 0x756e694c, // "Linu"
ecx: 0x564b2078, // "x KV"
edx: 0x7648204d, // "M Hv"
..Default::default()
})
.map_err(|_| Error::PopulatingCpuid)?;
cpuid
.push(CpuIdEntry {
function: 0x40000001,
eax: 0x31237648, // "Hv#1"
..Default::default()
})
.map_err(|_| Error::PopulatingCpuid)?;
cpuid
.push(CpuIdEntry {
function: 0x40000002,
eax: 0x3839, // "Build number"
ebx: 0xa0000, // "Version"
..Default::default()
})
.map_err(|_| Error::PopulatingCpuid)?;
cpuid
.push(CpuIdEntry {
function: 0x4000_0003,
eax: 1 << 1 // AccessPartitionReferenceCounter
| 1 << 2 // AccessSynicRegs
| 1 << 3 // AccessSyntheticTimerRegs
| 1 << 9, // AccessPartitionReferenceTsc
..Default::default()
})
.map_err(|_| Error::PopulatingCpuid)?;
for i in 0x4000_0004..=0x4000_000a {
cpuid
.push(CpuIdEntry {
function: i,
..Default::default()
})
.map_err(|_| Error::PopulatingCpuid)?;
}
}
// Copy CPU identification string
for i in 0x8000_0002..=0x8000_0004 {
cpuid.retain(|c| c.function != i);
let leaf = unsafe { x86_64::__cpuid(i) };
cpuid
.push(CpuIdEntry {
function: i,
eax: leaf.eax,
ebx: leaf.ebx,
ecx: leaf.ecx,
edx: leaf.edx,
..Default::default()
})
.map_err(|_| Error::PopulatingCpuid)?;
}
// Set CPU physical bits
for entry in cpuid.as_mut_slice().iter_mut() {
if entry.function == 0x8000_0008 {
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
}
}
fd.set_cpuid2(&cpuid)
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
@@ -425,7 +348,7 @@ pub fn configure_vcpu(
fd.enable_hyperv_synic().unwrap();
}
regs::setup_msrs(fd).map_err(Error::MSRSConfiguration)?;
regs::setup_msrs(fd).map_err(Error::MsrsConfiguration)?;
if let Some(kernel_entry_point) = kernel_entry_point {
// Safe to unwrap because this method is called after the VM is configured
regs::setup_regs(
@@ -435,10 +358,10 @@ pub fn configure_vcpu(
layout::ZERO_PAGE_START.raw_value(),
kernel_entry_point.protocol,
)
.map_err(Error::REGSConfiguration)?;
regs::setup_fpu(fd).map_err(Error::FPUConfiguration)?;
.map_err(Error::RegsConfiguration)?;
regs::setup_fpu(fd).map_err(Error::FpuConfiguration)?;
regs::setup_sregs(&vm_memory.memory(), fd, kernel_entry_point.protocol)
.map_err(Error::SREGSConfiguration)?;
.map_err(Error::SregsConfiguration)?;
}
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
Ok(())
@@ -521,7 +444,7 @@ pub fn configure_system(
// Check that the RAM is not smaller than the RSDP start address
if let Some(rsdp_addr) = rsdp_addr {
if rsdp_addr.0 > guest_mem.last_addr().0 {
return Err(super::Error::RSDPPastRamEnd);
return Err(super::Error::RsdpPastRamEnd);
}
}
@@ -536,6 +459,7 @@ pub fn configure_system(
)?;
}
BootProtocol::LinuxBoot => {
error!("Using deprecated LinuxBoot protocol: Please configure your kernel with CONFIG_PVH=y and supply the `vmlinux` file to `--kernel`");
configure_64bit_boot(
guest_mem,
cmdline_addr,
@@ -595,7 +519,7 @@ fn configure_pvh(
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
// Create the memory map entries.
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM)?;
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM);
let mem_end = guest_mem.last_addr();
@@ -605,21 +529,21 @@ fn configure_pvh(
layout::HIGH_RAM_START.raw_value(),
mem_end.unchecked_offset_from(layout::HIGH_RAM_START) + 1,
E820_RAM,
)?;
);
} else {
add_memmap_entry(
&mut memmap,
layout::HIGH_RAM_START.raw_value(),
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::HIGH_RAM_START),
E820_RAM,
)?;
);
if mem_end > layout::RAM_64BIT_START {
add_memmap_entry(
&mut memmap,
layout::RAM_64BIT_START.raw_value(),
mem_end.unchecked_offset_from(layout::RAM_64BIT_START) + 1,
E820_RAM,
)?;
);
}
}
@@ -628,7 +552,7 @@ fn configure_pvh(
layout::PCI_MMCONFIG_START.0,
layout::PCI_MMCONFIG_SIZE,
E820_RESERVED,
)?;
);
if let Some(sgx_epc_region) = sgx_epc_region {
add_memmap_entry(
@@ -636,7 +560,7 @@ fn configure_pvh(
sgx_epc_region.start().raw_value(),
sgx_epc_region.size() as u64,
E820_RESERVED,
)?;
);
}
start_info.0.memmap_entries = memmap.len() as u32;
@@ -681,12 +605,7 @@ fn configure_pvh(
Ok(())
}
fn add_memmap_entry(
memmap: &mut Vec<hvm_memmap_table_entry>,
addr: u64,
size: u64,
mem_type: u32,
) -> Result<(), Error> {
fn add_memmap_entry(memmap: &mut Vec<hvm_memmap_table_entry>, addr: u64, size: u64, mem_type: u32) {
// Add the table entry to the vector
memmap.push(hvm_memmap_table_entry {
addr,
@@ -694,8 +613,6 @@ fn add_memmap_entry(
type_: mem_type,
reserved: 0,
});
Ok(())
}
fn configure_64bit_boot(
@@ -975,7 +892,7 @@ mod tests {
#[test]
fn regions_lt_4gb() {
let regions = arch_memory_regions(1 << 29 as GuestUsize);
let regions = arch_memory_regions(1 << 29);
assert_eq!(3, regions.len());
assert_eq!(GuestAddress(0), regions[0].0);
assert_eq!(1usize << 29, regions[0].1);
@@ -983,7 +900,7 @@ mod tests {
#[test]
fn regions_gt_4gb() {
let regions = arch_memory_regions((1 << 32 as GuestUsize) + 0x8000);
let regions = arch_memory_regions((1 << 32) + 0x8000);
assert_eq!(4, regions.len());
assert_eq!(GuestAddress(0), regions[0].0);
assert_eq!(GuestAddress(1 << 32), regions[1].0);
@@ -992,7 +909,7 @@ mod tests {
#[test]
fn test_system_configuration() {
let no_vcpus = 4;
let gm = GuestMemoryMmap::from_ranges(&vec![(GuestAddress(0), 0x10000)]).unwrap();
let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let config_err = configure_system(
&gm,
GuestAddress(0),
@@ -1171,8 +1088,8 @@ mod tests {
},
];
add_memmap_entry(&mut memmap, 0, 0x1000, E820_RAM).unwrap();
add_memmap_entry(&mut memmap, 0x10000, 0xa000, E820_RESERVED).unwrap();
add_memmap_entry(&mut memmap, 0, 0x1000, E820_RAM);
add_memmap_entry(&mut memmap, 0x10000, 0xa000, E820_RESERVED);
assert_eq!(format!("{:?}", memmap), format!("{:?}", expected_memmap));
}

View File

@@ -9,8 +9,9 @@
use std::sync::Arc;
use std::{mem, result};
use super::gdt::{gdt_entry, segment_from_gdt};
use super::BootProtocol;
use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
use hypervisor::arch::x86::regs::*;
use hypervisor::x86_64::{FpuState, SpecialRegisters, StandardRegisters};
use layout::{
BOOT_GDT_START, BOOT_IDT_START, PDE_START, PDPTE_START, PML4_START, PML5_START, PVH_INFO_START,
@@ -24,25 +25,25 @@ pub enum Error {
/// Failed to set base registers for this CPU.
SetBaseRegisters(hypervisor::HypervisorCpuError),
/// Failed to configure the FPU.
SetFPURegisters(hypervisor::HypervisorCpuError),
SetFpuRegisters(hypervisor::HypervisorCpuError),
/// Setting up MSRs failed.
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
/// Failed to set SREGs for this CPU.
SetStatusRegisters(hypervisor::HypervisorCpuError),
/// Checking the GDT address failed.
CheckGDTAddr,
CheckGdtAddr,
/// Writing the GDT to RAM failed.
WriteGDT(GuestMemoryError),
WriteGdt(GuestMemoryError),
/// Writing the IDT to RAM failed.
WriteIDT(GuestMemoryError),
WriteIdt(GuestMemoryError),
/// Writing PDPTE to RAM failed.
WritePDPTEAddress(GuestMemoryError),
WritePdpteAddress(GuestMemoryError),
/// Writing PDE to RAM failed.
WritePDEAddress(GuestMemoryError),
WritePdeAddress(GuestMemoryError),
/// Writing PML4 to RAM failed.
WritePML4Address(GuestMemoryError),
WritePml4Address(GuestMemoryError),
/// Writing PML5 to RAM failed.
WritePML5Address(GuestMemoryError),
WritePml5Address(GuestMemoryError),
}
pub type Result<T> = result::Result<T, Error>;
@@ -59,7 +60,7 @@ pub fn setup_fpu(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
..Default::default()
};
vcpu.set_fpu(&fpu).map_err(Error::SetFPURegisters)
vcpu.set_fpu(&fpu).map_err(Error::SetFpuRegisters)
}
/// Configure Model Specific Registers (MSRs) for a given CPU.
@@ -134,21 +135,13 @@ pub fn setup_sregs(
const BOOT_GDT_MAX: usize = 4;
const EFER_LMA: u64 = 0x400;
const EFER_LME: u64 = 0x100;
const X86_CR0_PE: u64 = 0x1;
const X86_CR0_PG: u64 = 0x80000000;
const X86_CR4_PAE: u64 = 0x20;
const X86_CR4_LA57: u64 = 0x1000;
fn write_gdt_table(table: &[u64], guest_mem: &GuestMemoryMmap) -> Result<()> {
let boot_gdt_addr = BOOT_GDT_START;
for (index, entry) in table.iter().enumerate() {
let addr = guest_mem
.checked_offset(boot_gdt_addr, index * mem::size_of::<u64>())
.ok_or(Error::CheckGDTAddr)?;
guest_mem.write_obj(*entry, addr).map_err(Error::WriteGDT)?;
.ok_or(Error::CheckGdtAddr)?;
guest_mem.write_obj(*entry, addr).map_err(Error::WriteGdt)?;
}
Ok(())
}
@@ -157,7 +150,7 @@ fn write_idt_value(val: u64, guest_mem: &GuestMemoryMmap) -> Result<()> {
let boot_idt_addr = BOOT_IDT_START;
guest_mem
.write_obj(val, boot_idt_addr)
.map_err(Error::WriteIDT)
.map_err(Error::WriteIdt)
}
pub fn configure_segments_and_sregs(
@@ -209,12 +202,12 @@ pub fn configure_segments_and_sregs(
match boot_prot {
BootProtocol::PvhBoot => {
sregs.cr0 = X86_CR0_PE;
sregs.cr0 = CR0_PE;
sregs.cr4 = 0;
}
BootProtocol::LinuxBoot => {
/* 64-bit protected mode */
sregs.cr0 |= X86_CR0_PE;
sregs.cr0 |= CR0_PE;
sregs.efer |= EFER_LME | EFER_LMA;
}
}
@@ -227,31 +220,31 @@ pub fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut SpecialRegisters) ->
if unsafe { std::arch::x86_64::__cpuid(7).ecx } & (1 << 16) != 0 {
// Entry covering VA [0..256TB)
mem.write_obj(PML4_START.raw_value() | 0x03, PML5_START)
.map_err(Error::WritePML5Address)?;
.map_err(Error::WritePml5Address)?;
sregs.cr3 = PML5_START.raw_value();
sregs.cr4 |= X86_CR4_LA57;
sregs.cr4 |= CR4_LA57;
} else {
sregs.cr3 = PML4_START.raw_value();
}
// Entry covering VA [0..512GB)
mem.write_obj(PDPTE_START.raw_value() | 0x03, PML4_START)
.map_err(Error::WritePML4Address)?;
.map_err(Error::WritePml4Address)?;
// Entry covering VA [0..1GB)
mem.write_obj(PDE_START.raw_value() | 0x03, PDPTE_START)
.map_err(Error::WritePDPTEAddress)?;
.map_err(Error::WritePdpteAddress)?;
// 512 2MB entries together covering VA [0..1GB). Note we are assuming
// CPU supports 2MB pages (/proc/cpuinfo has 'pse'). All modern CPUs do.
for i in 0..512 {
mem.write_obj((i << 21) + 0x83u64, PDE_START.unchecked_add(i * 8))
.map_err(Error::WritePDEAddress)?;
.map_err(Error::WritePdeAddress)?;
}
sregs.cr4 |= X86_CR4_PAE;
sregs.cr0 |= X86_CR0_PG;
sregs.cr4 |= CR4_PAE;
sregs.cr0 |= CR0_PG;
Ok(())
}
@@ -264,7 +257,7 @@ mod tests {
use vm_memory::{GuestAddress, GuestMemoryMmap};
fn create_guest_mem() -> GuestMemoryMmap {
GuestMemoryMmap::from_ranges(&vec![(GuestAddress(0), 0x10000)]).unwrap()
GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap()
}
fn read_u64(gm: &GuestMemoryMmap, offset: GuestAddress) -> u64 {
@@ -301,7 +294,7 @@ mod tests {
assert_eq!(0, sregs.tr.base);
assert_eq!(0xffffffff, sregs.tr.limit);
assert_eq!(0, sregs.tr.avl);
assert_eq!(X86_CR0_PE, sregs.cr0);
assert_eq!(CR0_PE, sregs.cr0);
assert_eq!(EFER_LME | EFER_LMA, sregs.efer);
configure_segments_and_sregs(&gm, &mut sregs, BootProtocol::PvhBoot).unwrap();
@@ -331,7 +324,7 @@ mod tests {
assert_eq!(0x67, sregs.tr.limit);
assert_eq!(0xb, sregs.tr.type_);
assert_eq!(0, sregs.tr.avl);
assert_eq!(X86_CR0_PE, sregs.cr0);
assert_eq!(CR0_PE, sregs.cr0);
assert_eq!(0, sregs.cr4);
}
@@ -358,7 +351,7 @@ mod tests {
} else {
assert_eq!(PML4_START.raw_value(), sregs.cr3);
}
assert_eq!(X86_CR4_PAE, sregs.cr4);
assert_eq!(X86_CR0_PG, sregs.cr0);
assert_eq!(CR4_PAE, sregs.cr4);
assert_eq!(CR0_PG, sregs.cr0);
}
}

View File

@@ -157,7 +157,7 @@ fn write_string(
for c in val.as_bytes().iter() {
curptr = write_and_incr(mem, *c, curptr)?;
}
curptr = write_and_incr(mem, 0 as u8, curptr)?;
curptr = write_and_incr(mem, 0u8, curptr)?;
Ok(curptr)
}
@@ -170,55 +170,63 @@ pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<u64> {
{
handle += 1;
let mut smbios_biosinfo = SmbiosBiosInfo::default();
smbios_biosinfo.typ = BIOS_INFORMATION;
smbios_biosinfo.length = mem::size_of::<SmbiosBiosInfo>() as u8;
smbios_biosinfo.handle = handle;
smbios_biosinfo.vendor = 1; // First string written in this section
smbios_biosinfo.version = 2; // Second string written in this section
smbios_biosinfo.characteristics = PCI_SUPPORTED;
smbios_biosinfo.characteristics_ext2 = IS_VIRTUAL_MACHINE;
let smbios_biosinfo = SmbiosBiosInfo {
typ: BIOS_INFORMATION,
length: mem::size_of::<SmbiosBiosInfo>() as u8,
handle,
vendor: 1, // First string written in this section
version: 2, // Second string written in this section
characteristics: PCI_SUPPORTED,
characteristics_ext2: IS_VIRTUAL_MACHINE,
..Default::default()
};
curptr = write_and_incr(mem, smbios_biosinfo, curptr)?;
curptr = write_string(mem, "cloud-hypervisor", curptr)?;
curptr = write_string(mem, "0", curptr)?;
curptr = write_and_incr(mem, 0 as u8, curptr)?;
curptr = write_and_incr(mem, 0u8, curptr)?;
}
{
handle += 1;
let mut smbios_sysinfo = SmbiosSysInfo::default();
smbios_sysinfo.typ = SYSTEM_INFORMATION;
smbios_sysinfo.length = mem::size_of::<SmbiosSysInfo>() as u8;
smbios_sysinfo.handle = handle;
smbios_sysinfo.manufacturer = 1; // First string written in this section
smbios_sysinfo.product_name = 2; // Second string written in this section
let smbios_sysinfo = SmbiosSysInfo {
typ: SYSTEM_INFORMATION,
length: mem::size_of::<SmbiosSysInfo>() as u8,
handle,
manufacturer: 1, // First string written in this section
product_name: 2, // Second string written in this section
..Default::default()
};
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
curptr = write_string(mem, "Cloud Hypervisor", curptr)?;
curptr = write_string(mem, "cloud-hypervisor", curptr)?;
curptr = write_and_incr(mem, 0 as u8, curptr)?;
curptr = write_and_incr(mem, 0u8, curptr)?;
}
{
handle += 1;
let mut smbios_sysinfo = SmbiosSysInfo::default();
smbios_sysinfo.typ = END_OF_TABLE;
smbios_sysinfo.length = mem::size_of::<SmbiosSysInfo>() as u8;
smbios_sysinfo.handle = handle;
let smbios_sysinfo = SmbiosSysInfo {
typ: END_OF_TABLE,
length: mem::size_of::<SmbiosSysInfo>() as u8,
handle,
..Default::default()
};
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
curptr = write_and_incr(mem, 0 as u8, curptr)?;
curptr = write_and_incr(mem, 0u8, curptr)?;
}
{
let mut smbios_ep = Smbios30Entrypoint::default();
smbios_ep.signature = *SM3_MAGIC_IDENT;
smbios_ep.length = mem::size_of::<Smbios30Entrypoint>() as u8;
// SMBIOS rev 3.2.0
smbios_ep.majorver = 0x03;
smbios_ep.minorver = 0x02;
smbios_ep.docrev = 0x00;
smbios_ep.revision = 0x01; // SMBIOS 3.0
smbios_ep.max_size = curptr.unchecked_offset_from(physptr) as u32;
smbios_ep.physptr = physptr.0;
let mut smbios_ep = Smbios30Entrypoint {
signature: *SM3_MAGIC_IDENT,
length: mem::size_of::<Smbios30Entrypoint>() as u8,
// SMBIOS rev 3.2.0
majorver: 0x03,
minorver: 0x02,
docrev: 0x00,
revision: 0x01, // SMBIOS 3.0
max_size: curptr.unchecked_offset_from(physptr) as u32,
physptr: physptr.0,
..Default::default()
};
smbios_ep.checksum = compute_checksum(&smbios_ep);
mem.write_obj(smbios_ep, GuestAddress(SMBIOS_START))
.map_err(|_| Error::WriteSmbiosEp)?;

318
arch/src/x86_64/tdx/mod.rs Normal file
View File

@@ -0,0 +1,318 @@
// Copyright © 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use thiserror::Error;
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError, GuestMemoryMmap};
#[derive(Error, Debug)]
pub enum TdvfError {
#[error("Failed read TDVF descriptor: {0}")]
ReadDescriptor(#[source] std::io::Error),
#[error("Failed read TDVF descriptor offset: {0}")]
ReadDescriptorOffset(#[source] std::io::Error),
#[error("Invalid descriptor signature")]
InvalidDescriptorSignature,
#[error("Invalid descriptor size")]
InvalidDescriptorSize,
#[error("Invalid descriptor version")]
InvalidDescriptorVersion,
#[error("Failed to write HOB details to guest memory: {0}")]
GuestMemoryWriteHob(#[source] GuestMemoryError),
}
// TDVF_DESCRIPTOR
#[repr(packed)]
pub struct TdvfDescriptor {
signature: [u8; 4],
length: u32,
version: u32,
num_sections: u32, // NumberOfSectionEntry
}
// TDVF_SECTION
#[repr(packed)]
#[derive(Clone, Copy, Default, Debug)]
pub struct TdvfSection {
pub data_offset: u32,
pub data_size: u32, // RawDataSize
pub address: u64, // MemoryAddress
pub size: u64, // MemoryDataSize
pub r#type: TdvfSectionType,
pub attributes: u32,
}
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum TdvfSectionType {
Bfv,
Cfv,
TdHob,
TempMem,
Reserved = 0xffffffff,
}
impl Default for TdvfSectionType {
fn default() -> Self {
TdvfSectionType::Reserved
}
}
pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfError> {
// The 32-bit offset to the TDVF metadata is located 32 bytes from
// the end of the file.
// See "TDVF Metadata Pointer" in "TDX Virtual Firmware Design Guide
file.seek(SeekFrom::End(-0x20))
.map_err(TdvfError::ReadDescriptorOffset)?;
let mut descriptor_offset: [u8; 4] = [0; 4];
file.read_exact(&mut descriptor_offset)
.map_err(TdvfError::ReadDescriptorOffset)?;
let descriptor_offset = u32::from_le_bytes(descriptor_offset) as u64;
file.seek(SeekFrom::Start(descriptor_offset))
.map_err(TdvfError::ReadDescriptor)?;
let mut descriptor: TdvfDescriptor = unsafe { std::mem::zeroed() };
// Safe as we read exactly the size of the descriptor header
file.read_exact(unsafe {
std::slice::from_raw_parts_mut(
&mut descriptor as *mut _ as *mut u8,
std::mem::size_of::<TdvfDescriptor>(),
)
})
.map_err(TdvfError::ReadDescriptor)?;
if &descriptor.signature != b"TDVF" {
return Err(TdvfError::InvalidDescriptorSignature);
}
if descriptor.length as usize
!= std::mem::size_of::<TdvfDescriptor>()
+ std::mem::size_of::<TdvfSection>() * descriptor.num_sections as usize
{
return Err(TdvfError::InvalidDescriptorSize);
}
if descriptor.version != 1 {
return Err(TdvfError::InvalidDescriptorVersion);
}
let mut sections = Vec::new();
sections.resize_with(descriptor.num_sections as usize, TdvfSection::default);
// Safe as we read exactly the advertised sections
file.read_exact(unsafe {
std::slice::from_raw_parts_mut(
sections.as_mut_ptr() as *mut u8,
descriptor.num_sections as usize * std::mem::size_of::<TdvfSection>(),
)
})
.map_err(TdvfError::ReadDescriptor)?;
Ok(sections)
}
#[repr(u16)]
#[derive(Copy, Clone, Debug)]
enum HobType {
Handoff = 0x1,
ResourceDescriptor = 0x3,
Unused = 0xfffe,
EndOfHobList = 0xffff,
}
impl Default for HobType {
fn default() -> Self {
HobType::Unused
}
}
#[repr(C)]
#[derive(Copy, Clone, Default, Debug)]
struct HobHeader {
r#type: HobType,
length: u16,
reserved: u32,
}
unsafe impl ByteValued for HobHeader {}
#[repr(C)]
#[derive(Copy, Clone, Default, Debug)]
struct HobHandoffInfoTable {
header: HobHeader,
version: u32,
efi_memory_top: u64,
efi_memory_bottom: u64,
efi_free_memory_top: u64,
efi_free_memory_bottom: u64,
efi_end_of_hob_list: u64,
}
unsafe impl ByteValued for HobHandoffInfoTable {}
#[repr(C)]
#[derive(Copy, Clone, Default, Debug)]
struct EfiGuid {
data1: u32,
data2: u16,
data3: u16,
data4: [u8; 8],
}
#[repr(C)]
#[derive(Copy, Clone, Default, Debug)]
struct HobResourceDescriptor {
header: HobHeader,
owner: EfiGuid,
resource_type: u32,
resource_attribute: u32,
physical_start: u64,
resource_length: u64,
}
unsafe impl ByteValued for HobResourceDescriptor {}
pub struct TdHob {
start_offset: u64,
current_offset: u64,
}
fn align_hob(v: u64) -> u64 {
(v + 7) / 8 * 8
}
impl TdHob {
fn update_offset<T>(&mut self) {
self.current_offset = align_hob(self.current_offset + std::mem::size_of::<T>() as u64)
}
pub fn start(offset: u64) -> TdHob {
// Leave a gap to place the HandoffTable at the start as it can only be filled in later
let mut hob = TdHob {
start_offset: offset,
current_offset: offset,
};
hob.update_offset::<HobHandoffInfoTable>();
hob
}
pub fn finish(&mut self, mem: &GuestMemoryMmap) -> Result<(), TdvfError> {
// Write end
let end = HobHeader {
r#type: HobType::EndOfHobList,
length: std::mem::size_of::<HobHeader>() as u16,
reserved: 0,
};
info!("Writing HOB end {:x} {:x?}", self.current_offset, end);
mem.write_obj(end, GuestAddress(self.current_offset))
.map_err(TdvfError::GuestMemoryWriteHob)?;
self.update_offset::<HobHeader>();
// Write handoff, delayed as it needs end of HOB list
let efi_end_of_hob_list = self.current_offset;
let handoff = HobHandoffInfoTable {
header: HobHeader {
r#type: HobType::Handoff,
length: std::mem::size_of::<HobHandoffInfoTable>() as u16,
reserved: 0,
},
version: 0x9,
efi_memory_top: 0,
efi_memory_bottom: 0,
efi_free_memory_top: 0,
efi_free_memory_bottom: 0,
efi_end_of_hob_list,
};
info!("Writing HOB start {:x} {:x?}", self.start_offset, handoff);
mem.write_obj(handoff, GuestAddress(self.start_offset))
.map_err(TdvfError::GuestMemoryWriteHob)
}
pub fn add_resource(
&mut self,
mem: &GuestMemoryMmap,
physical_start: u64,
resource_length: u64,
resource_type: u32,
resource_attribute: u32,
) -> Result<(), TdvfError> {
let resource_descriptor = HobResourceDescriptor {
header: HobHeader {
r#type: HobType::ResourceDescriptor,
length: std::mem::size_of::<HobResourceDescriptor>() as u16,
reserved: 0,
},
owner: EfiGuid::default(),
resource_type,
resource_attribute,
physical_start,
resource_length,
};
info!(
"Writing HOB resource {:x} {:x?}",
self.current_offset, resource_descriptor
);
mem.write_obj(resource_descriptor, GuestAddress(self.current_offset))
.map_err(TdvfError::GuestMemoryWriteHob)?;
self.update_offset::<HobResourceDescriptor>();
Ok(())
}
pub fn add_memory_resource(
&mut self,
mem: &GuestMemoryMmap,
physical_start: u64,
resource_length: u64,
ram: bool,
) -> Result<(), TdvfError> {
self.add_resource(
mem,
physical_start,
resource_length,
if ram {
0 /* EFI_RESOURCE_SYSTEM_MEMORY */
} else {
0x5 /*EFI_RESOURCE_MEMORY_RESERVED */
},
/* TODO:
* QEMU currently fills it in like this:
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED
* which differs from the spec (due to TDVF implementation issue?)
*/
0x7,
)
}
pub fn add_mmio_resource(
&mut self,
mem: &GuestMemoryMmap,
physical_start: u64,
resource_length: u64,
) -> Result<(), TdvfError> {
self.add_resource(
mem,
physical_start,
resource_length,
0x1, /* EFI_RESOURCE_MEMORY_MAPPED_IO */
/*
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE
*/
0x403,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_parse_tdvf_sections() {
let mut f = std::fs::File::open("tdvf.fd").unwrap();
let sections = parse_tdvf_sections(&mut f).unwrap();
for section in sections {
eprintln!("{:x?}", section)
}
}
}

View File

@@ -8,14 +8,6 @@
#[allow(non_upper_case_globals)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[allow(
clippy::unreadable_literal,
clippy::redundant_static_lifetimes,
clippy::trivially_copy_pass_by_ref,
clippy::useless_transmute,
clippy::should_implement_trait,
clippy::transmute_ptr_to_ptr
)]
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
#[allow(clippy::unreadable_literal, clippy::redundant_static_lifetimes)]

View File

@@ -67,7 +67,7 @@ fn bindgen_test_layout_mpf_intel() {
concat!("Alignment of ", stringify!(mpf_intel))
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).signature as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).signature as *const _ as usize },
0usize,
concat!(
"Alignment of field: ",
@@ -77,7 +77,7 @@ fn bindgen_test_layout_mpf_intel() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).physptr as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).physptr as *const _ as usize },
4usize,
concat!(
"Alignment of field: ",
@@ -87,7 +87,7 @@ fn bindgen_test_layout_mpf_intel() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).length as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).length as *const _ as usize },
8usize,
concat!(
"Alignment of field: ",
@@ -97,7 +97,7 @@ fn bindgen_test_layout_mpf_intel() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).specification as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).specification as *const _ as usize },
9usize,
concat!(
"Alignment of field: ",
@@ -107,7 +107,7 @@ fn bindgen_test_layout_mpf_intel() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).checksum as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).checksum as *const _ as usize },
10usize,
concat!(
"Alignment of field: ",
@@ -117,7 +117,7 @@ fn bindgen_test_layout_mpf_intel() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).feature1 as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).feature1 as *const _ as usize },
11usize,
concat!(
"Alignment of field: ",
@@ -127,7 +127,7 @@ fn bindgen_test_layout_mpf_intel() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).feature2 as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).feature2 as *const _ as usize },
12usize,
concat!(
"Alignment of field: ",
@@ -137,7 +137,7 @@ fn bindgen_test_layout_mpf_intel() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).feature3 as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).feature3 as *const _ as usize },
13usize,
concat!(
"Alignment of field: ",
@@ -147,7 +147,7 @@ fn bindgen_test_layout_mpf_intel() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).feature4 as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).feature4 as *const _ as usize },
14usize,
concat!(
"Alignment of field: ",
@@ -157,7 +157,7 @@ fn bindgen_test_layout_mpf_intel() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpf_intel)).feature5 as *const _ as usize },
unsafe { &(*std::ptr::null::<mpf_intel>()).feature5 as *const _ as usize },
15usize,
concat!(
"Alignment of field: ",
@@ -200,7 +200,7 @@ fn bindgen_test_layout_mpc_table() {
concat!("Alignment of ", stringify!(mpc_table))
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).signature as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).signature as *const _ as usize },
0usize,
concat!(
"Alignment of field: ",
@@ -210,7 +210,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).length as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).length as *const _ as usize },
4usize,
concat!(
"Alignment of field: ",
@@ -220,7 +220,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).spec as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).spec as *const _ as usize },
6usize,
concat!(
"Alignment of field: ",
@@ -230,7 +230,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).checksum as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).checksum as *const _ as usize },
7usize,
concat!(
"Alignment of field: ",
@@ -240,7 +240,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).oem as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).oem as *const _ as usize },
8usize,
concat!(
"Alignment of field: ",
@@ -250,7 +250,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).productid as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).productid as *const _ as usize },
16usize,
concat!(
"Alignment of field: ",
@@ -260,7 +260,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).oemptr as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).oemptr as *const _ as usize },
28usize,
concat!(
"Alignment of field: ",
@@ -270,7 +270,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).oemsize as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).oemsize as *const _ as usize },
32usize,
concat!(
"Alignment of field: ",
@@ -280,7 +280,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).oemcount as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).oemcount as *const _ as usize },
34usize,
concat!(
"Alignment of field: ",
@@ -290,7 +290,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).lapic as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).lapic as *const _ as usize },
36usize,
concat!(
"Alignment of field: ",
@@ -300,7 +300,7 @@ fn bindgen_test_layout_mpc_table() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_table)).reserved as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_table>()).reserved as *const _ as usize },
40usize,
concat!(
"Alignment of field: ",
@@ -339,7 +339,7 @@ fn bindgen_test_layout_mpc_cpu() {
concat!("Alignment of ", stringify!(mpc_cpu))
);
assert_eq!(
unsafe { &(*(0 as *const mpc_cpu)).type_ as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_cpu>()).type_ as *const _ as usize },
0usize,
concat!(
"Alignment of field: ",
@@ -349,7 +349,7 @@ fn bindgen_test_layout_mpc_cpu() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_cpu)).apicid as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_cpu>()).apicid as *const _ as usize },
1usize,
concat!(
"Alignment of field: ",
@@ -359,7 +359,7 @@ fn bindgen_test_layout_mpc_cpu() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_cpu)).apicver as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_cpu>()).apicver as *const _ as usize },
2usize,
concat!(
"Alignment of field: ",
@@ -369,7 +369,7 @@ fn bindgen_test_layout_mpc_cpu() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_cpu)).cpuflag as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_cpu>()).cpuflag as *const _ as usize },
3usize,
concat!(
"Alignment of field: ",
@@ -379,7 +379,7 @@ fn bindgen_test_layout_mpc_cpu() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_cpu)).cpufeature as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_cpu>()).cpufeature as *const _ as usize },
4usize,
concat!(
"Alignment of field: ",
@@ -389,7 +389,7 @@ fn bindgen_test_layout_mpc_cpu() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_cpu)).featureflag as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_cpu>()).featureflag as *const _ as usize },
8usize,
concat!(
"Alignment of field: ",
@@ -399,7 +399,7 @@ fn bindgen_test_layout_mpc_cpu() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_cpu)).reserved as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_cpu>()).reserved as *const _ as usize },
12usize,
concat!(
"Alignment of field: ",
@@ -434,7 +434,7 @@ fn bindgen_test_layout_mpc_bus() {
concat!("Alignment of ", stringify!(mpc_bus))
);
assert_eq!(
unsafe { &(*(0 as *const mpc_bus)).type_ as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_bus>()).type_ as *const _ as usize },
0usize,
concat!(
"Alignment of field: ",
@@ -444,7 +444,7 @@ fn bindgen_test_layout_mpc_bus() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_bus)).busid as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_bus>()).busid as *const _ as usize },
1usize,
concat!(
"Alignment of field: ",
@@ -454,7 +454,7 @@ fn bindgen_test_layout_mpc_bus() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_bus)).bustype as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_bus>()).bustype as *const _ as usize },
2usize,
concat!(
"Alignment of field: ",
@@ -491,7 +491,7 @@ fn bindgen_test_layout_mpc_ioapic() {
concat!("Alignment of ", stringify!(mpc_ioapic))
);
assert_eq!(
unsafe { &(*(0 as *const mpc_ioapic)).type_ as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_ioapic>()).type_ as *const _ as usize },
0usize,
concat!(
"Alignment of field: ",
@@ -501,7 +501,7 @@ fn bindgen_test_layout_mpc_ioapic() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_ioapic)).apicid as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_ioapic>()).apicid as *const _ as usize },
1usize,
concat!(
"Alignment of field: ",
@@ -511,7 +511,7 @@ fn bindgen_test_layout_mpc_ioapic() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_ioapic)).apicver as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_ioapic>()).apicver as *const _ as usize },
2usize,
concat!(
"Alignment of field: ",
@@ -521,7 +521,7 @@ fn bindgen_test_layout_mpc_ioapic() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_ioapic)).flags as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_ioapic>()).flags as *const _ as usize },
3usize,
concat!(
"Alignment of field: ",
@@ -531,7 +531,7 @@ fn bindgen_test_layout_mpc_ioapic() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_ioapic)).apicaddr as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_ioapic>()).apicaddr as *const _ as usize },
4usize,
concat!(
"Alignment of field: ",
@@ -570,7 +570,7 @@ fn bindgen_test_layout_mpc_intsrc() {
concat!("Alignment of ", stringify!(mpc_intsrc))
);
assert_eq!(
unsafe { &(*(0 as *const mpc_intsrc)).type_ as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_intsrc>()).type_ as *const _ as usize },
0usize,
concat!(
"Alignment of field: ",
@@ -580,7 +580,7 @@ fn bindgen_test_layout_mpc_intsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_intsrc)).irqtype as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_intsrc>()).irqtype as *const _ as usize },
1usize,
concat!(
"Alignment of field: ",
@@ -590,7 +590,7 @@ fn bindgen_test_layout_mpc_intsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_intsrc)).irqflag as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_intsrc>()).irqflag as *const _ as usize },
2usize,
concat!(
"Alignment of field: ",
@@ -600,7 +600,7 @@ fn bindgen_test_layout_mpc_intsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_intsrc)).srcbus as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_intsrc>()).srcbus as *const _ as usize },
4usize,
concat!(
"Alignment of field: ",
@@ -610,7 +610,7 @@ fn bindgen_test_layout_mpc_intsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_intsrc)).srcbusirq as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_intsrc>()).srcbusirq as *const _ as usize },
5usize,
concat!(
"Alignment of field: ",
@@ -620,7 +620,7 @@ fn bindgen_test_layout_mpc_intsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_intsrc)).dstapic as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_intsrc>()).dstapic as *const _ as usize },
6usize,
concat!(
"Alignment of field: ",
@@ -630,7 +630,7 @@ fn bindgen_test_layout_mpc_intsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_intsrc)).dstirq as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_intsrc>()).dstirq as *const _ as usize },
7usize,
concat!(
"Alignment of field: ",
@@ -674,7 +674,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
concat!("Alignment of ", stringify!(mpc_lintsrc))
);
assert_eq!(
unsafe { &(*(0 as *const mpc_lintsrc)).type_ as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).type_ as *const _ as usize },
0usize,
concat!(
"Alignment of field: ",
@@ -684,7 +684,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_lintsrc)).irqtype as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).irqtype as *const _ as usize },
1usize,
concat!(
"Alignment of field: ",
@@ -694,7 +694,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_lintsrc)).irqflag as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).irqflag as *const _ as usize },
2usize,
concat!(
"Alignment of field: ",
@@ -704,7 +704,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_lintsrc)).srcbusid as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).srcbusid as *const _ as usize },
4usize,
concat!(
"Alignment of field: ",
@@ -714,7 +714,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_lintsrc)).srcbusirq as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).srcbusirq as *const _ as usize },
5usize,
concat!(
"Alignment of field: ",
@@ -724,7 +724,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_lintsrc)).destapic as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).destapic as *const _ as usize },
6usize,
concat!(
"Alignment of field: ",
@@ -734,7 +734,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_lintsrc)).destapiclint as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).destapiclint as *const _ as usize },
7usize,
concat!(
"Alignment of field: ",
@@ -771,7 +771,7 @@ fn bindgen_test_layout_mpc_oemtable() {
concat!("Alignment of ", stringify!(mpc_oemtable))
);
assert_eq!(
unsafe { &(*(0 as *const mpc_oemtable)).signature as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_oemtable>()).signature as *const _ as usize },
0usize,
concat!(
"Alignment of field: ",
@@ -781,7 +781,7 @@ fn bindgen_test_layout_mpc_oemtable() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_oemtable)).length as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_oemtable>()).length as *const _ as usize },
4usize,
concat!(
"Alignment of field: ",
@@ -791,7 +791,7 @@ fn bindgen_test_layout_mpc_oemtable() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_oemtable)).rev as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_oemtable>()).rev as *const _ as usize },
6usize,
concat!(
"Alignment of field: ",
@@ -801,7 +801,7 @@ fn bindgen_test_layout_mpc_oemtable() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_oemtable)).checksum as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_oemtable>()).checksum as *const _ as usize },
7usize,
concat!(
"Alignment of field: ",
@@ -811,7 +811,7 @@ fn bindgen_test_layout_mpc_oemtable() {
)
);
assert_eq!(
unsafe { &(*(0 as *const mpc_oemtable)).mpc as *const _ as usize },
unsafe { &(*std::ptr::null::<mpc_oemtable>()).mpc as *const _ as usize },
8usize,
concat!(
"Alignment of field: ",

View File

@@ -10,12 +10,15 @@ io_uring = []
[dependencies]
io-uring = ">=0.4.0"
libc = "0.2.80"
log = "0.4.11"
libc = "0.2.91"
log = "0.4.14"
qcow = { path = "../qcow" }
serde = ">=1.0.27"
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"
thiserror = "1.0"
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
vm-memory = { version = "0.3.0", features = ["backend-mmap", "backend-atomic"] }
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = ">=0.3.1"

View File

@@ -0,0 +1,56 @@
// Copyright © 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use thiserror::Error;
use vmm_sys_util::eventfd::EventFd;
#[derive(Error, Debug)]
pub enum DiskFileError {
/// Failed getting disk file size.
#[error("Failed getting disk file size: {0}")]
Size(#[source] std::io::Error),
/// Failed creating a new AsyncIo.
#[error("Failed creating a new AsyncIo: {0}")]
NewAsyncIo(#[source] std::io::Error),
}
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
pub trait DiskFile: Send + Sync {
fn size(&mut self) -> DiskFileResult<u64>;
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
}
#[derive(Error, Debug)]
pub enum AsyncIoError {
/// Failed vectored reading from file.
#[error("Failed vectored reading from file: {0}")]
ReadVectored(#[source] std::io::Error),
/// Failed vectored writing to file.
#[error("Failed vectored writing to file: {0}")]
WriteVectored(#[source] std::io::Error),
/// Failed synchronizing file.
#[error("Failed synchronizing file: {0}")]
Fsync(#[source] std::io::Error),
}
pub type AsyncIoResult<T> = std::result::Result<T, AsyncIoError>;
pub trait AsyncIo: Send + Sync {
fn notifier(&self) -> &EventFd;
fn read_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()>;
fn write_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()>;
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()>;
fn complete(&mut self) -> Vec<(u64, i32)>;
}

View File

@@ -0,0 +1,110 @@
// Copyright © 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
};
use crate::raw_async::RawFileAsync;
use crate::vhd::VhdFooter;
use std::fs::File;
use std::os::unix::io::{AsRawFd, RawFd};
use vmm_sys_util::eventfd::EventFd;
pub struct FixedVhdDiskAsync {
file: File,
size: u64,
}
impl FixedVhdDiskAsync {
pub fn new(mut file: File) -> std::io::Result<Self> {
let footer = VhdFooter::new(&mut file)?;
Ok(FixedVhdDiskAsync {
file,
size: footer.current_size(),
})
}
}
impl DiskFile for FixedVhdDiskAsync {
fn size(&mut self) -> DiskFileResult<u64> {
Ok(self.size)
}
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
Ok(Box::new(
FixedVhdAsync::new(self.file.as_raw_fd(), ring_depth, self.size)
.map_err(DiskFileError::NewAsyncIo)?,
) as Box<dyn AsyncIo>)
}
}
pub struct FixedVhdAsync {
raw_file_async: RawFileAsync,
size: u64,
}
impl FixedVhdAsync {
pub fn new(fd: RawFd, ring_depth: u32, size: u64) -> std::io::Result<Self> {
let raw_file_async = RawFileAsync::new(fd, ring_depth)?;
Ok(FixedVhdAsync {
raw_file_async,
size,
})
}
}
impl AsyncIo for FixedVhdAsync {
fn notifier(&self) -> &EventFd {
self.raw_file_async.notifier()
}
fn read_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
if offset as u64 >= self.size {
return Err(AsyncIoError::ReadVectored(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!(
"Invalid offset {}, can't be larger than file size {}",
offset, self.size
),
)));
}
self.raw_file_async.read_vectored(offset, iovecs, user_data)
}
fn write_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
if offset as u64 >= self.size {
return Err(AsyncIoError::WriteVectored(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!(
"Invalid offset {}, can't be larger than file size {}",
offset, self.size
),
)));
}
self.raw_file_async
.write_vectored(offset, iovecs, user_data)
}
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
self.raw_file_async.fsync(user_data)
}
fn complete(&mut self) -> Vec<(u64, i32)> {
self.raw_file_async.complete()
}
}

View File

@@ -0,0 +1,107 @@
// Copyright © 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
};
use crate::raw_sync::RawFileSync;
use crate::vhd::VhdFooter;
use std::fs::File;
use std::os::unix::io::{AsRawFd, RawFd};
use vmm_sys_util::eventfd::EventFd;
pub struct FixedVhdDiskSync {
file: File,
size: u64,
}
impl FixedVhdDiskSync {
pub fn new(mut file: File) -> std::io::Result<Self> {
let footer = VhdFooter::new(&mut file)?;
Ok(FixedVhdDiskSync {
file,
size: footer.current_size(),
})
}
}
impl DiskFile for FixedVhdDiskSync {
fn size(&mut self) -> DiskFileResult<u64> {
Ok(self.size)
}
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
Ok(Box::new(
FixedVhdSync::new(self.file.as_raw_fd(), self.size)
.map_err(DiskFileError::NewAsyncIo)?,
) as Box<dyn AsyncIo>)
}
}
pub struct FixedVhdSync {
raw_file_sync: RawFileSync,
size: u64,
}
impl FixedVhdSync {
pub fn new(fd: RawFd, size: u64) -> std::io::Result<Self> {
Ok(FixedVhdSync {
raw_file_sync: RawFileSync::new(fd),
size,
})
}
}
impl AsyncIo for FixedVhdSync {
fn notifier(&self) -> &EventFd {
self.raw_file_sync.notifier()
}
fn read_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
if offset as u64 >= self.size {
return Err(AsyncIoError::ReadVectored(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!(
"Invalid offset {}, can't be larger than file size {}",
offset, self.size
),
)));
}
self.raw_file_sync.read_vectored(offset, iovecs, user_data)
}
fn write_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
if offset as u64 >= self.size {
return Err(AsyncIoError::WriteVectored(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!(
"Invalid offset {}, can't be larger than file size {}",
offset, self.size
),
)));
}
self.raw_file_sync.write_vectored(offset, iovecs, user_data)
}
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
self.raw_file_sync.fsync(user_data)
}
fn complete(&mut self) -> Vec<(u64, i32)> {
self.raw_file_sync.complete()
}
}

View File

@@ -13,26 +13,35 @@ extern crate log;
#[macro_use]
extern crate serde_derive;
pub mod async_io;
pub mod fixed_vhd_async;
pub mod fixed_vhd_sync;
pub mod qcow_sync;
pub mod raw_async;
pub mod raw_sync;
pub mod vhd;
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, DiskFileError, DiskFileResult};
#[cfg(feature = "io_uring")]
use io_uring::Probe;
use io_uring::{opcode, squeue, IoUring};
use io_uring::{opcode, IoUring, Probe};
use serde::ser::{Serialize, SerializeStruct, Serializer};
use std::cmp;
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::convert::TryInto;
use std::fs::File;
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
use std::os::linux::fs::MetadataExt;
#[cfg(feature = "io_uring")]
use std::os::unix::io::AsRawFd;
use std::os::unix::io::RawFd;
use std::path::PathBuf;
use std::path::Path;
use std::result;
use std::sync::{Arc, Mutex};
use virtio_bindings::bindings::virtio_blk::*;
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap};
use vm_virtio::DescriptorChain;
#[cfg(feature = "io_uring")]
use vmm_sys_util::eventfd::EventFd;
const SECTOR_SHIFT: u8 = 9;
pub const SECTOR_SIZE: u64 = (0x01 as u64) << SECTOR_SHIFT;
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
#[derive(Debug)]
pub enum Error {
@@ -56,7 +65,7 @@ pub enum Error {
TooManyDescriptors,
}
fn build_device_id(disk_path: &PathBuf) -> result::Result<String, Error> {
fn build_device_id(disk_path: &Path) -> result::Result<String, Error> {
let blk_metadata = match disk_path.metadata() {
Err(_) => return Err(Error::GetFileMetadata),
Ok(m) => m,
@@ -71,7 +80,7 @@ fn build_device_id(disk_path: &PathBuf) -> result::Result<String, Error> {
Ok(device_id)
}
pub fn build_disk_image_id(disk_path: &PathBuf) -> Vec<u8> {
pub fn build_disk_image_id(disk_path: &Path) -> Vec<u8> {
let mut default_disk_image_id = vec![0; VIRTIO_BLK_ID_BYTES as usize];
match build_device_id(disk_path) {
Err(_) => {
@@ -98,6 +107,9 @@ pub enum ExecuteError {
Unsupported(u32),
SubmitIoUring(io::Error),
GetHostAddress(GuestMemoryError),
AsyncRead(AsyncIoError),
AsyncWrite(AsyncIoError),
AsyncFlush(AsyncIoError),
}
impl ExecuteError {
@@ -111,6 +123,9 @@ impl ExecuteError {
ExecuteError::Unsupported(_) => VIRTIO_BLK_S_UNSUPP,
ExecuteError::SubmitIoUring(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::GetHostAddress(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::AsyncRead(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::AsyncWrite(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::AsyncFlush(_) => VIRTIO_BLK_S_IOERR,
}
}
}
@@ -120,7 +135,7 @@ pub enum RequestType {
In,
Out,
Flush,
GetDeviceID,
GetDeviceId,
Unsupported(u32),
}
@@ -133,7 +148,7 @@ pub fn request_type(
VIRTIO_BLK_T_IN => Ok(RequestType::In),
VIRTIO_BLK_T_OUT => Ok(RequestType::Out),
VIRTIO_BLK_T_FLUSH => Ok(RequestType::Flush),
VIRTIO_BLK_T_GET_ID => Ok(RequestType::GetDeviceID),
VIRTIO_BLK_T_GET_ID => Ok(RequestType::GetDeviceId),
t => Ok(RequestType::Unsupported(t)),
}
}
@@ -148,6 +163,7 @@ fn sector(mem: &GuestMemoryMmap, desc_addr: GuestAddress) -> result::Result<u64,
mem.read_obj(addr).map_err(Error::GuestMemory)
}
#[derive(Debug)]
pub struct Request {
pub request_type: RequestType,
pub sector: u64,
@@ -177,12 +193,17 @@ impl Request {
let status_desc;
let mut desc = avail_desc
.next_descriptor()
.ok_or(Error::DescriptorChainTooShort)?;
.ok_or(Error::DescriptorChainTooShort)
.map_err(|e| {
error!("Only head descriptor present: request = {:?}", req);
e
})?;
if !desc.has_next() {
status_desc = desc;
// Only flush requests are allowed to skip the data descriptor.
if req.request_type != RequestType::Flush {
error!("Need a data descriptor: request = {:?}", req);
return Err(Error::DescriptorChainTooShort);
}
} else {
@@ -193,13 +214,17 @@ impl Request {
if !desc.is_write_only() && req.request_type == RequestType::In {
return Err(Error::UnexpectedReadOnlyDescriptor);
}
if !desc.is_write_only() && req.request_type == RequestType::GetDeviceID {
if !desc.is_write_only() && req.request_type == RequestType::GetDeviceId {
return Err(Error::UnexpectedReadOnlyDescriptor);
}
req.data_descriptors.push((desc.addr, desc.len));
desc = desc
.next_descriptor()
.ok_or(Error::DescriptorChainTooShort)?;
.ok_or(Error::DescriptorChainTooShort)
.map_err(|e| {
error!("DescriptorChain corrupted: request = {:?}", req);
e
})?;
}
status_desc = desc;
}
@@ -255,7 +280,7 @@ impl Request {
}
}
RequestType::Flush => disk.flush().map_err(ExecuteError::Flush)?,
RequestType::GetDeviceID => {
RequestType::GetDeviceId => {
if (*data_len as usize) < disk_id.len() {
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
}
@@ -268,12 +293,11 @@ impl Request {
Ok(len)
}
pub fn execute_io_uring(
pub fn execute_async(
&self,
mem: &GuestMemoryMmap,
io_uring: &mut IoUring,
disk_nsectors: u64,
disk_image_fd: RawFd,
disk_image: &mut dyn AsyncIo,
disk_id: &[u8],
user_data: u64,
) -> result::Result<bool, ExecuteError> {
@@ -281,9 +305,6 @@ impl Request {
let request_type = self.request_type;
let offset = (sector << SECTOR_SHIFT) as libc::off_t;
let (submitter, sq, _) = io_uring.split();
let mut avail_sq = sq.available();
let mut iovecs = Vec::new();
for (data_addr, data_len) in &self.data_descriptors {
let mut top: u64 = u64::from(*data_len) / SECTOR_SIZE;
@@ -311,51 +332,21 @@ impl Request {
// Queue operations expected to be submitted.
match request_type {
RequestType::In => {
// Safe because we know the file descriptor is valid and we
// relied on vm-memory to provide the buffer address.
let _ = unsafe {
avail_sq.push(
opcode::Readv::new(
opcode::types::Fd(disk_image_fd),
iovecs.as_ptr(),
iovecs.len() as u32,
)
.offset(offset)
.build()
.flags(squeue::Flags::ASYNC)
.user_data(user_data),
)
};
disk_image
.read_vectored(offset, iovecs, user_data)
.map_err(ExecuteError::AsyncRead)?;
}
RequestType::Out => {
// Safe because we know the file descriptor is valid and we
// relied on vm-memory to provide the buffer address.
let _ = unsafe {
avail_sq.push(
opcode::Writev::new(
opcode::types::Fd(disk_image_fd),
iovecs.as_ptr(),
iovecs.len() as u32,
)
.offset(offset)
.build()
.flags(squeue::Flags::ASYNC)
.user_data(user_data),
)
};
disk_image
.write_vectored(offset, iovecs, user_data)
.map_err(ExecuteError::AsyncWrite)?;
}
RequestType::Flush => {
// Safe because we know the file descriptor is valid.
let _ = unsafe {
avail_sq.push(
opcode::Fsync::new(opcode::types::Fd(disk_image_fd))
.build()
.flags(squeue::Flags::ASYNC)
.user_data(user_data),
)
};
disk_image
.fsync(Some(user_data))
.map_err(ExecuteError::AsyncFlush)?;
}
RequestType::GetDeviceID => {
RequestType::GetDeviceId => {
let (data_addr, data_len) = if self.data_descriptors.len() == 1 {
(self.data_descriptors[0].0, self.data_descriptors[0].1)
} else {
@@ -371,11 +362,6 @@ impl Request {
RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
}
// Update the submission queue and submit new operations to the
// io_uring instance.
avail_sq.sync();
submitter.submit().map_err(ExecuteError::SubmitIoUring)?;
Ok(true)
}
@@ -571,3 +557,145 @@ pub fn block_io_uring_is_supported() -> bool {
pub fn block_io_uring_is_supported() -> bool {
false
}
pub fn disk_size(file: &mut dyn Seek, semaphore: &mut Arc<Mutex<()>>) -> DiskFileResult<u64> {
// Take the semaphore to ensure other threads are not interacting with
// the underlying file.
let _lock = semaphore.lock().unwrap();
Ok(file.seek(SeekFrom::End(0)).map_err(DiskFileError::Size)? as u64)
}
pub trait ReadSeekFile: Read + Seek {}
impl<F: Read + Seek> ReadSeekFile for F {}
pub fn read_vectored_sync(
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
file: &mut dyn ReadSeekFile,
eventfd: &EventFd,
completion_list: &mut Vec<(u64, i32)>,
semaphore: &mut Arc<Mutex<()>>,
) -> AsyncIoResult<()> {
// Convert libc::iovec into IoSliceMut
let mut slices = Vec::new();
for iovec in iovecs.iter() {
slices.push(IoSliceMut::new(unsafe { std::mem::transmute(*iovec) }));
}
let result = {
// Take the semaphore to ensure other threads are not interacting
// with the underlying file.
let _lock = semaphore.lock().unwrap();
// Move the cursor to the right offset
file.seek(SeekFrom::Start(offset as u64))
.map_err(AsyncIoError::ReadVectored)?;
// Read vectored
file.read_vectored(slices.as_mut_slice())
.map_err(AsyncIoError::ReadVectored)?
};
completion_list.push((user_data, result as i32));
eventfd.write(1).unwrap();
Ok(())
}
pub trait WriteSeekFile: Write + Seek {}
impl<F: Write + Seek> WriteSeekFile for F {}
pub fn write_vectored_sync(
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
file: &mut dyn WriteSeekFile,
eventfd: &EventFd,
completion_list: &mut Vec<(u64, i32)>,
semaphore: &mut Arc<Mutex<()>>,
) -> AsyncIoResult<()> {
// Convert libc::iovec into IoSlice
let mut slices = Vec::new();
for iovec in iovecs.iter() {
slices.push(IoSlice::new(unsafe { std::mem::transmute(*iovec) }));
}
let result = {
// Take the semaphore to ensure other threads are not interacting
// with the underlying file.
let _lock = semaphore.lock().unwrap();
// Move the cursor to the right offset
file.seek(SeekFrom::Start(offset as u64))
.map_err(AsyncIoError::WriteVectored)?;
// Write vectored
file.write_vectored(slices.as_slice())
.map_err(AsyncIoError::WriteVectored)?
};
completion_list.push((user_data, result as i32));
eventfd.write(1).unwrap();
Ok(())
}
pub fn fsync_sync(
user_data: Option<u64>,
file: &mut dyn Write,
eventfd: &EventFd,
completion_list: &mut Vec<(u64, i32)>,
semaphore: &mut Arc<Mutex<()>>,
) -> AsyncIoResult<()> {
let result: i32 = {
// Take the semaphore to ensure other threads are not interacting
// with the underlying file.
let _lock = semaphore.lock().unwrap();
// Flush
file.flush().map_err(AsyncIoError::Fsync)?;
0
};
if let Some(user_data) = user_data {
completion_list.push((user_data, result));
eventfd.write(1).unwrap();
}
Ok(())
}
pub enum ImageType {
FixedVhd,
Qcow2,
Raw,
}
const QCOW_MAGIC: u32 = 0x5146_49fb;
/// Determine image type through file parsing.
pub fn detect_image_type(f: &mut File) -> std::io::Result<ImageType> {
// We must create a buffer aligned on 512 bytes with a size being a
// multiple of 512 bytes as the file might be opened with O_DIRECT flag.
#[repr(align(512))]
struct Sector {
data: [u8; 512],
}
let mut s = Sector { data: [0; 512] };
f.read_exact(&mut s.data)?;
// Check 4 first bytes to get the header value and determine the image type
let image_type = if u32::from_be_bytes(s.data[0..4].try_into().unwrap()) == QCOW_MAGIC {
ImageType::Qcow2
} else if vhd::is_fixed_vhd(f)? {
ImageType::FixedVhd
} else {
ImageType::Raw
};
Ok(image_type)
}

111
block_util/src/qcow_sync.rs Normal file
View File

@@ -0,0 +1,111 @@
// Copyright © 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileResult};
use crate::{disk_size, fsync_sync, read_vectored_sync, write_vectored_sync};
use qcow::{QcowFile, RawFile};
use std::fs::File;
use std::sync::{Arc, Mutex};
use vmm_sys_util::eventfd::EventFd;
pub struct QcowDiskSync {
qcow_file: QcowFile,
semaphore: Arc<Mutex<()>>,
}
impl QcowDiskSync {
pub fn new(file: File, direct_io: bool) -> Self {
QcowDiskSync {
qcow_file: QcowFile::from(RawFile::new(file, direct_io))
.expect("Failed creating QcowFile"),
semaphore: Arc::new(Mutex::new(())),
}
}
}
impl DiskFile for QcowDiskSync {
fn size(&mut self) -> DiskFileResult<u64> {
disk_size(&mut self.qcow_file, &mut self.semaphore)
}
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
Ok(Box::new(QcowSync::new(
self.qcow_file.clone(),
self.semaphore.clone(),
)) as Box<dyn AsyncIo>)
}
}
pub struct QcowSync {
qcow_file: QcowFile,
eventfd: EventFd,
completion_list: Vec<(u64, i32)>,
semaphore: Arc<Mutex<()>>,
}
impl QcowSync {
pub fn new(qcow_file: QcowFile, semaphore: Arc<Mutex<()>>) -> Self {
QcowSync {
qcow_file,
eventfd: EventFd::new(libc::EFD_NONBLOCK)
.expect("Failed creating EventFd for QcowSync"),
completion_list: Vec::new(),
semaphore,
}
}
}
impl AsyncIo for QcowSync {
fn notifier(&self) -> &EventFd {
&self.eventfd
}
fn read_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
read_vectored_sync(
offset,
iovecs,
user_data,
&mut self.qcow_file,
&self.eventfd,
&mut self.completion_list,
&mut self.semaphore,
)
}
fn write_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
write_vectored_sync(
offset,
iovecs,
user_data,
&mut self.qcow_file,
&self.eventfd,
&mut self.completion_list,
&mut self.semaphore,
)
}
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
fsync_sync(
user_data,
&mut self.qcow_file,
&self.eventfd,
&mut self.completion_list,
&mut self.semaphore,
)
}
fn complete(&mut self) -> Vec<(u64, i32)> {
self.completion_list.drain(..).collect()
}
}

159
block_util/src/raw_async.rs Normal file
View File

@@ -0,0 +1,159 @@
// Copyright © 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
};
use io_uring::{opcode, squeue, types, IoUring};
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::os::unix::io::{AsRawFd, RawFd};
use vmm_sys_util::eventfd::EventFd;
pub struct RawFileDisk {
file: File,
}
impl RawFileDisk {
pub fn new(file: File) -> Self {
RawFileDisk { file }
}
}
impl DiskFile for RawFileDisk {
fn size(&mut self) -> DiskFileResult<u64> {
Ok(self
.file
.seek(SeekFrom::End(0))
.map_err(DiskFileError::Size)? as u64)
}
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
Ok(Box::new(
RawFileAsync::new(self.file.as_raw_fd(), ring_depth)
.map_err(DiskFileError::NewAsyncIo)?,
) as Box<dyn AsyncIo>)
}
}
pub struct RawFileAsync {
fd: RawFd,
io_uring: IoUring,
eventfd: EventFd,
}
impl RawFileAsync {
pub fn new(fd: RawFd, ring_depth: u32) -> std::io::Result<Self> {
let io_uring = IoUring::new(ring_depth)?;
let eventfd = EventFd::new(libc::EFD_NONBLOCK)?;
// Register the io_uring eventfd that will notify when something in
// the completion queue is ready.
io_uring.submitter().register_eventfd(eventfd.as_raw_fd())?;
Ok(RawFileAsync {
fd,
io_uring,
eventfd,
})
}
}
impl AsyncIo for RawFileAsync {
fn notifier(&self) -> &EventFd {
&self.eventfd
}
fn read_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
let (submitter, mut sq, _) = self.io_uring.split();
// Safe because we know the file descriptor is valid and we
// relied on vm-memory to provide the buffer address.
let _ = unsafe {
sq.push(
&opcode::Readv::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
.offset(offset)
.build()
.flags(squeue::Flags::ASYNC)
.user_data(user_data),
)
};
// Update the submission queue and submit new operations to the
// io_uring instance.
sq.sync();
submitter.submit().map_err(AsyncIoError::ReadVectored)?;
Ok(())
}
fn write_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
let (submitter, mut sq, _) = self.io_uring.split();
// Safe because we know the file descriptor is valid and we
// relied on vm-memory to provide the buffer address.
let _ = unsafe {
sq.push(
&opcode::Writev::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
.offset(offset)
.build()
.flags(squeue::Flags::ASYNC)
.user_data(user_data),
)
};
// Update the submission queue and submit new operations to the
// io_uring instance.
sq.sync();
submitter.submit().map_err(AsyncIoError::WriteVectored)?;
Ok(())
}
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
if let Some(user_data) = user_data {
let (submitter, mut sq, _) = self.io_uring.split();
// Safe because we know the file descriptor is valid.
let _ = unsafe {
sq.push(
&opcode::Fsync::new(types::Fd(self.fd))
.build()
.flags(squeue::Flags::ASYNC)
.user_data(user_data),
)
};
// Update the submission queue and submit new operations to the
// io_uring instance.
sq.sync();
submitter.submit().map_err(AsyncIoError::Fsync)?;
} else {
unsafe { libc::fsync(self.fd) };
}
Ok(())
}
fn complete(&mut self) -> Vec<(u64, i32)> {
let mut completion_list = Vec::new();
let cq = self.io_uring.completion();
for cq_entry in cq {
completion_list.push((cq_entry.user_data(), cq_entry.result()));
}
completion_list
}
}

122
block_util/src/raw_sync.rs Normal file
View File

@@ -0,0 +1,122 @@
// Copyright © 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
};
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::os::unix::io::{AsRawFd, RawFd};
use vmm_sys_util::eventfd::EventFd;
pub struct RawFileDiskSync {
file: File,
}
impl RawFileDiskSync {
pub fn new(file: File) -> Self {
RawFileDiskSync { file }
}
}
impl DiskFile for RawFileDiskSync {
fn size(&mut self) -> DiskFileResult<u64> {
Ok(self
.file
.seek(SeekFrom::End(0))
.map_err(DiskFileError::Size)? as u64)
}
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
Ok(Box::new(RawFileSync::new(self.file.as_raw_fd())) as Box<dyn AsyncIo>)
}
}
pub struct RawFileSync {
fd: RawFd,
eventfd: EventFd,
completion_list: Vec<(u64, i32)>,
}
impl RawFileSync {
pub fn new(fd: RawFd) -> Self {
RawFileSync {
fd,
eventfd: EventFd::new(libc::EFD_NONBLOCK).expect("Failed creating EventFd for RawFile"),
completion_list: Vec::new(),
}
}
}
impl AsyncIo for RawFileSync {
fn notifier(&self) -> &EventFd {
&self.eventfd
}
fn read_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
let result = unsafe {
libc::preadv(
self.fd as libc::c_int,
iovecs.as_ptr() as *const libc::iovec,
iovecs.len() as libc::c_int,
offset,
)
};
if result < 0 {
return Err(AsyncIoError::ReadVectored(std::io::Error::last_os_error()));
}
self.completion_list.push((user_data, result as i32));
self.eventfd.write(1).unwrap();
Ok(())
}
fn write_vectored(
&mut self,
offset: libc::off_t,
iovecs: Vec<libc::iovec>,
user_data: u64,
) -> AsyncIoResult<()> {
let result = unsafe {
libc::pwritev(
self.fd as libc::c_int,
iovecs.as_ptr() as *const libc::iovec,
iovecs.len() as libc::c_int,
offset,
)
};
if result < 0 {
return Err(AsyncIoError::WriteVectored(std::io::Error::last_os_error()));
}
self.completion_list.push((user_data, result as i32));
self.eventfd.write(1).unwrap();
Ok(())
}
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
let result = unsafe { libc::fsync(self.fd as libc::c_int) };
if result < 0 {
return Err(AsyncIoError::Fsync(std::io::Error::last_os_error()));
}
if let Some(user_data) = user_data {
self.completion_list.push((user_data, result as i32));
self.eventfd.write(1).unwrap();
}
Ok(())
}
fn complete(&mut self) -> Vec<(u64, i32)> {
self.completion_list.drain(..).collect()
}
}

221
block_util/src/vhd.rs Normal file
View File

@@ -0,0 +1,221 @@
// Copyright © 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryInto;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
#[derive(Clone, Copy)]
pub struct VhdFooter {
cookie: u64,
features: u32,
file_format_version: u32,
data_offset: u64,
time_stamp: u32,
creator_application: u32,
creator_version: u32,
creator_host_os: u32,
original_size: u64,
current_size: u64,
disk_geometry: u32,
disk_type: u32,
checksum: u32,
unique_id: u128,
saved_state: u8,
}
impl VhdFooter {
pub fn new(file: &mut File) -> std::io::Result<VhdFooter> {
// We must create a buffer aligned on 512 bytes with a size being a
// multiple of 512 bytes as the file might be opened with O_DIRECT flag.
#[repr(align(512))]
struct Sector {
data: [u8; 512],
}
let mut s = Sector { data: [0; 512] };
// Place the cursor 512 bytes before the end of the file, as this is
// where the footer starts.
file.seek(SeekFrom::End(-512))?;
// Fill in the VhdFooter structure
file.read_exact(&mut s.data)?;
Ok(VhdFooter {
cookie: u64::from_be_bytes(s.data[0..8].try_into().unwrap()),
features: u32::from_be_bytes(s.data[8..12].try_into().unwrap()),
file_format_version: u32::from_be_bytes(s.data[12..16].try_into().unwrap()),
data_offset: u64::from_be_bytes(s.data[16..24].try_into().unwrap()),
time_stamp: u32::from_be_bytes(s.data[24..28].try_into().unwrap()),
creator_application: u32::from_be_bytes(s.data[28..32].try_into().unwrap()),
creator_version: u32::from_be_bytes(s.data[32..36].try_into().unwrap()),
creator_host_os: u32::from_be_bytes(s.data[36..40].try_into().unwrap()),
original_size: u64::from_be_bytes(s.data[40..48].try_into().unwrap()),
current_size: u64::from_be_bytes(s.data[48..56].try_into().unwrap()),
disk_geometry: u32::from_be_bytes(s.data[56..60].try_into().unwrap()),
disk_type: u32::from_be_bytes(s.data[60..64].try_into().unwrap()),
checksum: u32::from_be_bytes(s.data[64..68].try_into().unwrap()),
unique_id: u128::from_be_bytes(s.data[68..84].try_into().unwrap()),
saved_state: u8::from_be_bytes(s.data[84..85].try_into().unwrap()),
})
}
pub fn cookie(&self) -> u64 {
self.cookie
}
pub fn features(&self) -> u32 {
self.features
}
pub fn file_format_version(&self) -> u32 {
self.file_format_version
}
pub fn data_offset(&self) -> u64 {
self.data_offset
}
pub fn time_stamp(&self) -> u32 {
self.time_stamp
}
pub fn creator_application(&self) -> u32 {
self.creator_application
}
pub fn creator_version(&self) -> u32 {
self.creator_version
}
pub fn creator_host_os(&self) -> u32 {
self.creator_host_os
}
pub fn original_size(&self) -> u64 {
self.original_size
}
pub fn current_size(&self) -> u64 {
self.current_size
}
pub fn disk_geometry(&self) -> u32 {
self.disk_geometry
}
pub fn disk_type(&self) -> u32 {
self.disk_type
}
pub fn checksum(&self) -> u32 {
self.checksum
}
pub fn unique_id(&self) -> u128 {
self.unique_id
}
pub fn saved_state(&self) -> u8 {
self.saved_state
}
}
/// Determine image type through file parsing.
pub fn is_fixed_vhd(f: &mut File) -> std::io::Result<bool> {
let footer = VhdFooter::new(f)?;
// "conectix" => 0x636f6e6563746978
Ok(footer.cookie() == 0x636f6e6563746978
&& footer.file_format_version() == 0x0001_0000
&& footer.data_offset() == 0xffff_ffff_ffff_ffff
&& footer.disk_type() == 0x2)
}
#[cfg(test)]
mod tests {
use super::{is_fixed_vhd, VhdFooter};
use std::fs::File;
use std::io::{Seek, SeekFrom, Write};
use vmm_sys_util::tempfile::TempFile;
fn valid_fixed_vhd_footer() -> Vec<u8> {
vec![
0x63, 0x6f, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x78, // cookie
0x00, 0x00, 0x00, 0x02, // features
0x00, 0x01, 0x00, 0x00, // file format version
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // data offset
0x27, 0xa6, 0xa6, 0x5d, // time stamp
0x71, 0x65, 0x6d, 0x75, // creator application
0x00, 0x05, 0x00, 0x03, // creator version
0x57, 0x69, 0x32, 0x6b, // creator host os
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // original size
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // current size
0x11, 0xe0, 0x10, 0x3f, // disk geometry
0x00, 0x00, 0x00, 0x02, // disk type
0x00, 0x00, 0x00, 0x00, // checksum
0x98, 0x7b, 0xb1, 0xcd, 0x84, 0x14, 0x41, 0xfc, 0xa4, 0xab, 0xd0, 0x69, 0x45, 0x2b,
0xf2, 0x23, // unique id
0x00, // saved state
]
}
fn valid_dynamic_vhd_footer() -> Vec<u8> {
vec![
0x63, 0x6f, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x78, // cookie
0x00, 0x00, 0x00, 0x02, // features
0x00, 0x01, 0x00, 0x00, // file format version
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data offset
0x27, 0xa6, 0xa6, 0x5d, // time stamp
0x71, 0x65, 0x6d, 0x75, // creator application
0x00, 0x05, 0x00, 0x03, // creator version
0x57, 0x69, 0x32, 0x6b, // creator host os
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // original size
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // current size
0x11, 0xe0, 0x10, 0x3f, // disk geometry
0x00, 0x00, 0x00, 0x03, // disk type
0x00, 0x00, 0x00, 0x00, // checksum
0x98, 0x7b, 0xb1, 0xcd, 0x84, 0x14, 0x41, 0xfc, 0xa4, 0xab, 0xd0, 0x69, 0x45, 0x2b,
0xf2, 0x23, // unique id
0x00, // saved state
]
}
fn with_file<F>(footer: &[u8], mut testfn: F)
where
F: FnMut(File),
{
let mut disk_file: File = TempFile::new().unwrap().into_file();
disk_file.set_len(0x1000_0200).unwrap();
disk_file.seek(SeekFrom::Start(0x1000_0000)).unwrap();
disk_file.write_all(&footer).unwrap();
testfn(disk_file); // File closed when the function exits.
}
#[test]
fn test_check_vhd_footer() {
with_file(&valid_fixed_vhd_footer(), |mut file: File| {
let vhd_footer = VhdFooter::new(&mut file).expect("Failed to create VHD footer");
assert_eq!(vhd_footer.cookie(), 0x636f_6e65_6374_6978);
assert_eq!(vhd_footer.features(), 0x0000_0002);
assert_eq!(vhd_footer.file_format_version(), 0x0001_0000);
assert_eq!(vhd_footer.data_offset(), 0xffff_ffff_ffff_ffff);
assert_eq!(vhd_footer.time_stamp(), 0x27a6_a65d);
assert_eq!(vhd_footer.creator_application(), 0x7165_6d75);
assert_eq!(vhd_footer.creator_version(), 0x0005_0003);
assert_eq!(vhd_footer.creator_host_os(), 0x5769_326b);
assert_eq!(vhd_footer.original_size(), 0x0000_0000_1000_0000);
assert_eq!(vhd_footer.current_size(), 0x0000_0000_1000_0000);
assert_eq!(vhd_footer.disk_geometry(), 0x11e0_103f);
assert_eq!(vhd_footer.disk_type(), 0x0000_0002);
assert_eq!(vhd_footer.checksum(), 0x0000_0000);
assert_eq!(
vhd_footer.unique_id(),
0x987b_b1cd_8414_41fc_a4ab_d069_452b_f223
);
assert_eq!(vhd_footer.saved_state(), 0x00);
});
}
#[test]
fn test_is_fixed_vhd() {
with_file(&valid_fixed_vhd_footer(), |mut file: File| {
assert!(is_fixed_vhd(&mut file).unwrap());
});
}
#[test]
fn test_is_not_fixed_vhd() {
with_file(&valid_dynamic_vhd_footer(), |mut file: File| {
assert!(!(is_fixed_vhd(&mut file).unwrap()));
});
}
}

View File

@@ -6,22 +6,19 @@ authors = ["The Chromium OS Authors"]
[dependencies]
anyhow = "1.0"
bitflags = ">=1.2.1"
byteorder = "1.3.4"
byteorder = "1.4.3"
epoll = ">=4.0.1"
libc = "0.2.80"
log = "0.4.11"
libc = "0.2.91"
log = "0.4.14"
serde = {version = ">=1.0.27", features = ["rc"] }
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"
vm-device = { path = "../vm-device" }
acpi_tables = { path = "../acpi_tables", optional = true }
vm-memory = "0.3.0"
vm-memory = "0.5.0"
vm-migration = { path = "../vm-migration" }
vmm-sys-util = ">=0.3.1"
[dev-dependencies]
tempfile = "3.1.0"
[features]
default = []
acpi = ["acpi_tables"]

View File

@@ -4,12 +4,15 @@
//
use acpi_tables::{aml, aml::Aml};
use std::sync::Arc;
use std::sync::{Arc, Barrier};
use std::time::Instant;
use vm_device::interrupt::InterruptSourceGroup;
use vm_device::BusDevice;
use vm_memory::GuestAddress;
use vmm_sys_util::eventfd::EventFd;
use HotPlugNotificationFlags;
use AcpiNotificationFlags;
pub const GED_DEVICE_ACPI_SIZE: usize = 0x1;
/// A device for handling ACPI shutdown and reboot
pub struct AcpiShutdownDevice {
@@ -36,7 +39,7 @@ impl BusDevice for AcpiShutdownDevice {
}
}
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) {
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if data[0] == 1 {
debug!("ACPI Reboot signalled");
if let Err(e) = self.reset_evt.write(1) {
@@ -54,28 +57,35 @@ impl BusDevice for AcpiShutdownDevice {
error!("Error triggering ACPI shutdown event: {}", e);
}
}
None
}
}
/// A device for handling ACPI GED event generation
pub struct AcpiGEDDevice {
pub struct AcpiGedDevice {
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
notification_type: HotPlugNotificationFlags,
notification_type: AcpiNotificationFlags,
ged_irq: u32,
address: GuestAddress,
}
impl AcpiGEDDevice {
pub fn new(interrupt: Arc<Box<dyn InterruptSourceGroup>>, ged_irq: u32) -> AcpiGEDDevice {
AcpiGEDDevice {
impl AcpiGedDevice {
pub fn new(
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
ged_irq: u32,
address: GuestAddress,
) -> AcpiGedDevice {
AcpiGedDevice {
interrupt,
notification_type: HotPlugNotificationFlags::NO_DEVICES_CHANGED,
notification_type: AcpiNotificationFlags::NO_DEVICES_CHANGED,
ged_irq,
address,
}
}
pub fn notify(
&mut self,
notification_type: HotPlugNotificationFlags,
notification_type: AcpiNotificationFlags,
) -> Result<(), std::io::Error> {
self.notification_type |= notification_type;
self.interrupt.trigger(0)
@@ -87,18 +97,16 @@ impl AcpiGEDDevice {
}
// I/O port reports what type of notification was made
impl BusDevice for AcpiGEDDevice {
impl BusDevice for AcpiGedDevice {
// Spec has all fields as zero
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
data[0] = self.notification_type.bits();
self.notification_type = HotPlugNotificationFlags::NO_DEVICES_CHANGED;
self.notification_type = AcpiNotificationFlags::NO_DEVICES_CHANGED;
}
fn write(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
}
#[cfg(feature = "acpi")]
impl Aml for AcpiGEDDevice {
impl Aml for AcpiGedDevice {
fn to_aml_bytes(&self) -> Vec<u8> {
aml::Device::new(
"_SB_.GED_".into(),
@@ -115,7 +123,12 @@ impl Aml for AcpiGEDDevice {
self.ged_irq,
)]),
),
&aml::OpRegion::new("GDST".into(), aml::OpRegionSpace::SystemIO, 0xb000, 0x1),
&aml::OpRegion::new(
"GDST".into(),
aml::OpRegionSpace::SystemMemory,
self.address.0 as usize,
GED_DEVICE_ACPI_SIZE,
),
&aml::Field::new(
"GDST".into(),
aml::FieldAccessType::Byte,
@@ -143,6 +156,14 @@ impl Aml for AcpiGEDDevice {
&aml::Equal::new(&aml::Local(1), &4usize),
vec![&aml::MethodCall::new("\\_SB_.PCI0.PCNT".into(), vec![])],
),
&aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
&aml::If::new(
&aml::Equal::new(&aml::Local(1), &8usize),
vec![&aml::Notify::new(
&aml::Path::new("\\_SB_.PWRB"),
&0x80usize,
)],
),
],
),
],
@@ -151,11 +172,11 @@ impl Aml for AcpiGEDDevice {
}
}
pub struct AcpiPMTimerDevice {
pub struct AcpiPmTimerDevice {
start: Instant,
}
impl AcpiPMTimerDevice {
impl AcpiPmTimerDevice {
pub fn new() -> Self {
Self {
start: Instant::now(),
@@ -163,13 +184,13 @@ impl AcpiPMTimerDevice {
}
}
impl Default for AcpiPMTimerDevice {
impl Default for AcpiPmTimerDevice {
fn default() -> Self {
Self::new()
}
}
impl BusDevice for AcpiPMTimerDevice {
impl BusDevice for AcpiPmTimerDevice {
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
let now = Instant::now();
let since = now.duration_since(self.start);
@@ -183,6 +204,4 @@ impl BusDevice for AcpiPMTimerDevice {
data.copy_from_slice(&counter.to_le_bytes());
}
fn write(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
}

View File

@@ -6,15 +6,16 @@ use super::interrupt_controller::{Error, InterruptController};
use std::result;
use std::sync::Arc;
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
LegacyIrqSourceConfig, MsiIrqGroupConfig,
};
use vmm_sys_util::eventfd::EventFd;
type Result<T> = result::Result<T, Error>;
// Reserve 32 IRQs (GSI 32 ~ 64) for legacy device.
// GsiAllocator should allocate beyond this: from 64 on
// Reserve 32 IRQs for legacy device.
pub const IRQ_LEGACY_BASE: usize = 0;
pub const IRQ_LEGACY_COUNT: usize = 32;
pub const IRQ_SPI_OFFSET: usize = 32;
// This Gic struct implements InterruptController to provide interrupt delivery service.
// The Gic source files in arch/ folder maintain the Aarch64 specific Gic device.
@@ -33,7 +34,7 @@ impl Gic {
) -> Result<Gic> {
let interrupt_source_group = interrupt_manager
.create_group(MsiIrqGroupConfig {
base: IRQ_SPI_OFFSET as InterruptIndex,
base: IRQ_LEGACY_BASE as InterruptIndex,
count: IRQ_LEGACY_COUNT as InterruptIndex,
})
.map_err(Error::CreateInterruptSourceGroup)?;
@@ -46,9 +47,26 @@ impl Gic {
impl InterruptController for Gic {
fn enable(&self) -> Result<()> {
// Set irqfd for legacy interrupts
self.interrupt_source_group
.enable()
.map_err(Error::EnableInterrupt)?;
// Set irq_routing for legacy interrupts.
// irqchip: Hardcode to 0 as we support only 1 GIC
// pin: Use irq number as pin
for i in IRQ_LEGACY_BASE..(IRQ_LEGACY_BASE + IRQ_LEGACY_COUNT) {
let config = LegacyIrqSourceConfig {
irqchip: 0,
pin: i as u32,
};
self.interrupt_source_group
.update(
i as InterruptIndex,
InterruptSourceConfig::LegacyIrq(config),
)
.map_err(Error::EnableInterrupt)?;
}
Ok(())
}
@@ -61,4 +79,8 @@ impl InterruptController for Gic {
Ok(())
}
fn notifier(&self, irq: usize) -> Option<EventFd> {
self.interrupt_source_group.notifier(irq as InterruptIndex)
}
}

View File

@@ -4,6 +4,7 @@
use std::io;
use std::result;
use vmm_sys_util::eventfd::EventFd;
#[derive(Debug)]
pub enum Error {
@@ -56,4 +57,5 @@ pub trait InterruptController: Send {
fn enable(&self) -> Result<()>;
#[cfg(target_arch = "x86_64")]
fn end_of_interrupt(&mut self, vec: u8);
fn notifier(&self, irq: usize) -> Option<EventFd>;
}

View File

@@ -13,7 +13,7 @@ use super::interrupt_controller::{Error, InterruptController};
use anyhow::anyhow;
use byteorder::{ByteOrder, LittleEndian};
use std::result;
use std::sync::Arc;
use std::sync::{Arc, Barrier};
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
MsiIrqGroupConfig, MsiIrqSourceConfig,
@@ -24,6 +24,7 @@ use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
Transportable,
};
use vmm_sys_util::eventfd::EventFd;
#[derive(Serialize, Deserialize)]
#[serde(remote = "GuestAddress")]
@@ -112,9 +113,9 @@ enum TriggerMode {
enum DeliveryMode {
Fixed = 0b000,
Lowest = 0b001,
SMI = 0b010, // System management interrupt
Smi = 0b010, // System management interrupt
RemoteRead = 0b011, // This is no longer supported by intel.
NMI = 0b100, // Non maskable interrupt
Nmi = 0b100, // Non maskable interrupt
Init = 0b101,
Startup = 0b110,
External = 0b111,
@@ -167,7 +168,7 @@ impl BusDevice for Ioapic {
LittleEndian::write_u32(data, value);
}
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
assert!(data.len() == 4);
debug!("IOAPIC_W @ offset 0x{:x}", offset);
@@ -181,6 +182,7 @@ impl BusDevice for Ioapic {
error!("IOAPIC: failed writing at offset {}", offset);
}
}
None
}
}
@@ -192,7 +194,7 @@ impl Ioapic {
) -> Result<Ioapic> {
let interrupt_source_group = interrupt_manager
.create_group(MsiIrqGroupConfig {
base: 0 as InterruptIndex,
base: 0,
count: NUM_IOAPIC_PINS as InterruptIndex,
})
.map_err(Error::CreateInterruptSourceGroup)?;
@@ -331,9 +333,9 @@ impl Ioapic {
match delivery_mode {
x if (x == DeliveryMode::Fixed as u8)
|| (x == DeliveryMode::Lowest as u8)
|| (x == DeliveryMode::SMI as u8)
|| (x == DeliveryMode::Smi as u8)
|| (x == DeliveryMode::RemoteRead as u8)
|| (x == DeliveryMode::NMI as u8)
|| (x == DeliveryMode::Nmi as u8)
|| (x == DeliveryMode::Init as u8)
|| (x == DeliveryMode::Startup as u8)
|| (x == DeliveryMode::External as u8) => {}
@@ -404,6 +406,10 @@ impl InterruptController for Ioapic {
Ok(())
}
fn notifier(&self, irq: usize) -> Option<EventFd> {
self.interrupt_source_group.notifier(irq as InterruptIndex)
}
}
impl Snapshottable for Ioapic {

View File

@@ -5,6 +5,7 @@
use libc::{clock_gettime, gmtime_r, time_t, timespec, tm, CLOCK_REALTIME};
use std::cmp::min;
use std::mem;
use std::sync::{Arc, Barrier};
use vm_device::BusDevice;
const INDEX_MASK: u8 = 0x7f;
@@ -44,16 +45,17 @@ impl Cmos {
}
impl BusDevice for Cmos {
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if data.len() != 1 {
return;
return None;
}
match offset {
INDEX_OFFSET => self.index = data[0] & INDEX_MASK,
DATA_OFFSET => self.data[self.index as usize] = data[0],
o => panic!("bad write offset on CMOS device: {}", o),
}
};
None
}
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {

View File

@@ -7,6 +7,7 @@
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
//
use std::sync::{Arc, Barrier};
use vm_device::BusDevice;
/// Provides firmware debug output via I/O port controls
@@ -30,11 +31,13 @@ impl BusDevice for FwDebugDevice {
}
}
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) {
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if data.len() == 1 {
print!("{}", data[0] as char);
} else {
error!("Invalid write size on debug port: {}", data.len())
}
None
}
}

View File

@@ -0,0 +1,484 @@
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
//! ARM PrimeCell General Purpose Input/Output(PL061)
//!
//! This module implements an ARM PrimeCell General Purpose Input/Output(PL061) to support gracefully poweroff microvm from external.
//!
use crate::{read_le_u32, write_le_u32};
use anyhow::anyhow;
use std::result;
use std::sync::{Arc, Barrier};
use std::{fmt, io};
use vm_device::interrupt::InterruptSourceGroup;
use vm_device::BusDevice;
use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
Transportable,
};
const OFS_DATA: u64 = 0x400; // Data Register
const GPIODIR: u64 = 0x400; // Direction Register
const GPIOIS: u64 = 0x404; // Interrupt Sense Register
const GPIOIBE: u64 = 0x408; // Interrupt Both Edges Register
const GPIOIEV: u64 = 0x40c; // Interrupt Event Register
const GPIOIE: u64 = 0x410; // Interrupt Mask Register
const GPIORIE: u64 = 0x414; // Raw Interrupt Status Register
const GPIOMIS: u64 = 0x418; // Masked Interrupt Status Register
const GPIOIC: u64 = 0x41c; // Interrupt Clear Register
const GPIOAFSEL: u64 = 0x420; // Mode Control Select Register
// From 0x424 to 0xFDC => reserved space.
// From 0xFE0 to 0xFFC => Peripheral and PrimeCell Identification Registers which are Read Only registers.
// Thses registers can conceptually be treated as a 32-bit register, and PartNumber[11:0] is used to identify the peripheral.
// We are putting the expected values (look at 'Reset value' column from above mentioned document) in an array.
const GPIO_ID: [u8; 8] = [0x61, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
// ID Margins
const GPIO_ID_LOW: u64 = 0xfe0;
const GPIO_ID_HIGH: u64 = 0x1000;
const N_GPIOS: u32 = 8;
#[derive(Debug)]
pub enum Error {
BadWriteOffset(u64),
GpioInterruptDisabled,
GpioInterruptFailure(io::Error),
GpioTriggerKeyFailure(u32),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {}", offset),
Error::GpioInterruptDisabled => write!(f, "GPIO interrupt disabled by guest driver.",),
Error::GpioInterruptFailure(ref e) => {
write!(f, "Could not trigger GPIO interrupt: {}.", e)
}
Error::GpioTriggerKeyFailure(key) => {
write!(f, "Invalid GPIO Input key triggerd: {}.", key)
}
}
}
}
type Result<T> = result::Result<T, Error>;
/// A GPIO device following the PL061 specification.
pub struct Gpio {
id: String,
// Data Register
data: u32,
old_in_data: u32,
// Direction Register
dir: u32,
// Interrupt Sense Register
isense: u32,
// Interrupt Both Edges Register
ibe: u32,
// Interrupt Event Register
iev: u32,
// Interrupt Mask Register
im: u32,
// Raw Interrupt Status Register
istate: u32,
// Mode Control Select Register
afsel: u32,
// GPIO irq_field
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
}
#[derive(Serialize, Deserialize)]
pub struct GpioState {
data: u32,
old_in_data: u32,
dir: u32,
isense: u32,
ibe: u32,
iev: u32,
im: u32,
istate: u32,
afsel: u32,
}
impl Gpio {
/// Constructs an PL061 GPIO device.
pub fn new(id: String, interrupt: Arc<Box<dyn InterruptSourceGroup>>) -> Self {
Self {
id,
data: 0,
old_in_data: 0,
dir: 0,
isense: 0,
ibe: 0,
iev: 0,
im: 0,
istate: 0,
afsel: 0,
interrupt,
}
}
fn state(&self) -> GpioState {
GpioState {
data: self.data,
old_in_data: self.old_in_data,
dir: self.dir,
isense: self.isense,
ibe: self.ibe,
iev: self.iev,
im: self.im,
istate: self.istate,
afsel: self.afsel,
}
}
fn set_state(&mut self, state: &GpioState) {
self.data = state.data;
self.old_in_data = state.old_in_data;
self.dir = state.dir;
self.isense = state.isense;
self.ibe = state.ibe;
self.iev = state.iev;
self.im = state.im;
self.istate = state.istate;
self.afsel = state.afsel;
}
fn pl061_internal_update(&mut self) {
// FIXME:
// Missing Output Interrupt Emulation.
// Input Edging Interrupt Emulation.
let changed = ((self.old_in_data ^ self.data) & !self.dir) as u32;
if changed > 0 {
self.old_in_data = self.data;
for i in 0..N_GPIOS {
let mask = (1 << i) as u32;
if (changed & mask) > 0 {
// Bits set high in GPIOIS(Interrupt sense register) configure the corresponding
// pins to detect levels, otherwise, detect edges.
if (self.isense & mask) == 0 {
if (self.ibe & mask) > 0 {
// Bits set high in GPIOIBE(Interrupt both-edges register) configure the corresponding
// pins to detect both falling and rising edges.
// Clearing a bit configures the pin to be controlled by GPIOIEV.
self.istate |= mask;
} else {
// Bits set to high in GPIOIEV(Interrupt event register) configure the
// corresponding pin to detect rising edges, otherwise, detect falling edges.
self.istate |= !(self.data ^ self.iev) & mask;
}
}
}
}
}
// Input Level Interrupt Emulation.
self.istate |= !(self.data ^ self.iev) & self.isense;
}
fn handle_write(&mut self, offset: u64, val: u32) -> Result<()> {
if offset < OFS_DATA {
// In order to write to data register, the corresponding bits in the mask, resulting
// from the offsite[9:2], must be HIGH. otherwise the bit values remain unchanged.
let mask = (offset >> 2) as u32 & self.dir;
self.data = (self.data & !mask) | (val & mask);
} else {
match offset {
GPIODIR => {
/* Direction Register */
self.dir = val & 0xff;
}
GPIOIS => {
/* Interrupt Sense Register */
self.isense = val & 0xff;
}
GPIOIBE => {
/* Interrupt Both Edges Register */
self.ibe = val & 0xff;
}
GPIOIEV => {
/* Interrupt Event Register */
self.iev = val & 0xff;
}
GPIOIE => {
/* Interrupt Mask Register */
self.im = val & 0xff;
}
GPIOIC => {
/* Interrupt Clear Register */
self.istate &= !val;
}
GPIOAFSEL => {
/* Mode Control Select Register */
self.afsel = val & 0xff;
}
o => {
return Err(Error::BadWriteOffset(o));
}
}
}
Ok(())
}
pub fn trigger_key(&mut self, key: u32) -> Result<()> {
let mask = (1 << key) as u32;
if (!self.dir & mask) > 0 {
// emulate key event
// By default, Input Pin is configured to detect both rising and falling edges.
// So reverse the input pin data to generate a pulse.
self.data |= !(self.data & mask) & mask;
self.pl061_internal_update();
match self.trigger_gpio_interrupt() {
Ok(_) | Err(Error::GpioInterruptDisabled) => return Ok(()),
Err(e) => return Err(e),
}
}
Err(Error::GpioTriggerKeyFailure(key))
}
fn trigger_gpio_interrupt(&self) -> Result<()> {
// Bits set to high in GPIOIE(Interrupt mask register) allow the corresponding pins to
// trigger their individual interrupts and then the combined GPIOINTR line.
if (self.istate & self.im) == 0 {
warn!("Failed to trigger GPIO input interrupt (disabled by guest OS)");
return Err(Error::GpioInterruptDisabled);
}
self.interrupt
.trigger(0)
.map_err(Error::GpioInterruptFailure)?;
Ok(())
}
}
impl BusDevice for Gpio {
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
let value;
let mut read_ok = true;
if (GPIO_ID_LOW..GPIO_ID_HIGH).contains(&offset) {
let index = ((offset - GPIO_ID_LOW) >> 2) as usize;
value = u32::from(GPIO_ID[index]);
} else if offset < OFS_DATA {
value = self.data & ((offset >> 2) as u32)
} else {
value = match offset {
GPIODIR => self.dir,
GPIOIS => self.isense,
GPIOIBE => self.ibe,
GPIOIEV => self.iev,
GPIOIE => self.im,
GPIORIE => self.istate,
GPIOMIS => self.istate & self.im,
GPIOAFSEL => self.afsel,
_ => {
read_ok = false;
0
}
};
}
if read_ok && data.len() <= 4 {
write_le_u32(data, value);
} else {
warn!(
"Invalid GPIO PL061 read: offset {}, data length {}",
offset,
data.len()
);
}
}
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if data.len() <= 4 {
let value = read_le_u32(&data);
if let Err(e) = self.handle_write(offset, value) {
warn!("Failed to write to GPIO PL061 device: {}", e);
}
} else {
warn!(
"Invalid GPIO PL061 write: offset {}, data length {}",
offset,
data.len()
);
}
None
}
}
impl Snapshottable for Gpio {
fn id(&self) -> String {
self.id.clone()
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
let snapshot =
serde_json::to_vec(&self.state()).map_err(|e| MigratableError::Snapshot(e.into()))?;
let mut gpio_snapshot = Snapshot::new(self.id.as_str());
gpio_snapshot.add_data_section(SnapshotDataSection {
id: format!("{}-section", self.id),
snapshot,
});
Ok(gpio_snapshot)
}
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
if let Some(gpio_section) = snapshot.snapshot_data.get(&format!("{}-section", self.id)) {
let gpio_state = match serde_json::from_slice(&gpio_section.snapshot) {
Ok(state) => state,
Err(error) => {
return Err(MigratableError::Restore(anyhow!(
"Could not deserialize GPIO {}",
error
)))
}
};
self.set_state(&gpio_state);
return Ok(());
}
Err(MigratableError::Restore(anyhow!(
"Could not find the GPIO snapshot section"
)))
}
}
impl Pausable for Gpio {}
impl Transportable for Gpio {}
impl Migratable for Gpio {}
#[cfg(test)]
mod tests {
use super::*;
use crate::{read_le_u32, write_le_u32};
use std::sync::Arc;
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
use vmm_sys_util::eventfd::EventFd;
const GPIO_NAME: &str = "gpio";
const LEGACY_GPIO_MAPPED_IO_START: u64 = 0x0902_0000;
struct TestInterrupt {
event_fd: EventFd,
}
impl InterruptSourceGroup for TestInterrupt {
fn trigger(&self, _index: InterruptIndex) -> result::Result<(), std::io::Error> {
self.event_fd.write(1)
}
fn update(
&self,
_index: InterruptIndex,
_config: InterruptSourceConfig,
) -> result::Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap())
}
}
impl TestInterrupt {
fn new(event_fd: EventFd) -> Self {
TestInterrupt { event_fd }
}
}
#[test]
fn test_gpio_read_write_and_event() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut gpio = Gpio::new(
String::from(GPIO_NAME),
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
);
let mut data = [0; 4];
// Read and write to the GPIODIR register.
// Set pin 0 output pin.
write_le_u32(&mut data, 1);
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &mut data);
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &mut data);
let v = read_le_u32(&data);
assert_eq!(v, 1);
// Read and write to the GPIODATA register.
write_le_u32(&mut data, 1);
// Set pin 0 high.
let offset = 0x00000004 as u64;
gpio.write(LEGACY_GPIO_MAPPED_IO_START, offset, &mut data);
gpio.read(LEGACY_GPIO_MAPPED_IO_START, offset, &mut data);
let v = read_le_u32(&data);
assert_eq!(v, 1);
// Read and write to the GPIOIS register.
// Configure pin 0 detecting level interrupt.
write_le_u32(&mut data, 1);
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &mut data);
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &mut data);
let v = read_le_u32(&data);
assert_eq!(v, 1);
// Read and write to the GPIOIBE register.
// Configure pin 1 detecting both falling and rising edges.
write_le_u32(&mut data, 2);
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &mut data);
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &mut data);
let v = read_le_u32(&data);
assert_eq!(v, 2);
// Read and write to the GPIOIEV register.
// Configure pin 2 detecting both falling and rising edges.
write_le_u32(&mut data, 4);
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &mut data);
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &mut data);
let v = read_le_u32(&data);
assert_eq!(v, 4);
// Read and write to the GPIOIE register.
// Configure pin 0...2 capable of triggering their individual interrupts
// and then the combined GPIOINTR line.
write_le_u32(&mut data, 7);
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &mut data);
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &mut data);
let v = read_le_u32(&data);
assert_eq!(v, 7);
let mask = 0x00000002 as u32;
// emulate an rising pulse in pin 1.
gpio.data |= !(gpio.data & mask) & mask;
gpio.pl061_internal_update();
// The interrupt line on pin 1 should be on.
// Read the GPIOMIS register.
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOMIS, &mut data);
let v = read_le_u32(&data);
assert_eq!(v, 2);
// Read and Write to the GPIOIC register.
// clear interrupt in pin 1.
write_le_u32(&mut data, 2);
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &mut data);
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &mut data);
let v = read_le_u32(&data);
assert_eq!(v, 2);
// Attempts to write beyond the writable space.
write_le_u32(&mut data, 0);
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &mut data);
let mut data = [0; 4];
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &mut data);
let index = GPIO_ID_LOW + 3;
assert_eq!(data[0], GPIO_ID[((index - GPIO_ID_LOW) >> 2) as usize]);
}
}

View File

@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use vmm_sys_util::eventfd::EventFd;
use std::sync::{Arc, Barrier};
use vm_device::BusDevice;
use vmm_sys_util::eventfd::EventFd;
/// A i8042 PS/2 controller that emulates just enough to shutdown the machine.
pub struct I8042Device {
@@ -32,12 +32,14 @@ impl BusDevice for I8042Device {
}
}
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if data.len() == 1 && data[0] == 0xfe && offset == 3 {
debug!("i8042 reset signalled");
if let Err(e) = self.reset_evt.write(1) {
error!("Error triggering i8042 reset event: {}", e);
}
}
None
}
}

View File

@@ -9,10 +9,14 @@
mod cmos;
#[cfg(feature = "fwdebug")]
mod fwdebug;
#[cfg(target_arch = "aarch64")]
mod gpio_pl061;
mod i8042;
#[cfg(target_arch = "aarch64")]
mod rtc_pl031;
mod serial;
#[cfg(target_arch = "aarch64")]
mod uart_pl011;
#[cfg(feature = "cmos")]
pub use self::cmos::Cmos;
@@ -22,4 +26,10 @@ pub use self::i8042::I8042Device;
pub use self::serial::Serial;
#[cfg(target_arch = "aarch64")]
pub use self::rtc_pl031::RTC;
pub use self::gpio_pl061::Error as GpioDeviceError;
#[cfg(target_arch = "aarch64")]
pub use self::gpio_pl061::Gpio;
#[cfg(target_arch = "aarch64")]
pub use self::rtc_pl031::Rtc;
#[cfg(target_arch = "aarch64")]
pub use self::uart_pl011::Pl011;

View File

@@ -8,8 +8,9 @@
//! This is achieved by generating an interrupt signal after counting for a programmed number of cycles of
//! a real-time clock input.
//!
use crate::{read_le_u32, write_le_u32};
use std::fmt;
use std::sync::Arc;
use std::sync::{Arc, Barrier};
use std::time::Instant;
use std::{io, result};
use vm_device::interrupt::InterruptSourceGroup;
@@ -38,52 +39,6 @@ const AMBA_ID_HIGH: u64 = 0x1000;
/// Constant to convert seconds to nanoseconds.
pub const NANOS_PER_SECOND: u64 = 1_000_000_000;
#[allow(unused_macros)]
macro_rules! generate_read_fn {
($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => {
#[allow(dead_code)]
pub fn $fn_name(input: &[$byte_type]) -> $data_type {
assert!($type_size == std::mem::size_of::<$data_type>());
let mut array = [0u8; $type_size];
for (byte, read) in array.iter_mut().zip(input.iter().cloned()) {
*byte = read as u8;
}
<$data_type>::$endian_type(array)
}
};
}
#[allow(unused_macros)]
macro_rules! generate_write_fn {
($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => {
#[allow(dead_code)]
pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) {
for (byte, read) in buf
.iter_mut()
.zip(<$data_type>::$endian_type(n).iter().cloned())
{
*byte = read as $byte_type;
}
}
};
}
generate_read_fn!(read_le_u16, u16, u8, 2, from_le_bytes);
generate_read_fn!(read_le_u32, u32, u8, 4, from_le_bytes);
generate_read_fn!(read_le_u64, u64, u8, 8, from_le_bytes);
generate_read_fn!(read_le_i32, i32, i8, 4, from_le_bytes);
generate_read_fn!(read_be_u16, u16, u8, 2, from_be_bytes);
generate_read_fn!(read_be_u32, u32, u8, 4, from_be_bytes);
generate_write_fn!(write_le_u16, u16, u8, to_le_bytes);
generate_write_fn!(write_le_u32, u32, u8, to_le_bytes);
generate_write_fn!(write_le_u64, u64, u8, to_le_bytes);
generate_write_fn!(write_le_i32, i32, i8, to_le_bytes);
generate_write_fn!(write_be_u16, u16, u8, to_be_bytes);
generate_write_fn!(write_be_u32, u32, u8, to_be_bytes);
#[derive(Debug)]
pub enum Error {
BadWriteOffset(u64),
@@ -115,9 +70,9 @@ pub enum ClockType {
ThreadCpu,
}
impl Into<libc::clockid_t> for ClockType {
fn into(self) -> libc::clockid_t {
match self {
impl From<ClockType> for libc::clockid_t {
fn from(ct: ClockType) -> libc::clockid_t {
match ct {
ClockType::Monotonic => libc::CLOCK_MONOTONIC,
ClockType::Real => libc::CLOCK_REALTIME,
ClockType::ProcessCpu => libc::CLOCK_PROCESS_CPUTIME_ID,
@@ -260,7 +215,7 @@ pub fn seconds_to_nanoseconds(value: i64) -> Option<i64> {
}
/// A RTC device following the PL031 specification..
pub struct RTC {
pub struct Rtc {
previous_now: Instant,
tick_offset: i64,
// This is used for implementing the RTC alarm. However, in Firecracker we do not need it.
@@ -272,10 +227,10 @@ pub struct RTC {
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
}
impl RTC {
impl Rtc {
/// Constructs an AMBA PL031 RTC device.
pub fn new(interrupt: Arc<Box<dyn InterruptSourceGroup>>) -> RTC {
RTC {
pub fn new(interrupt: Arc<Box<dyn InterruptSourceGroup>>) -> Self {
Self {
// This is used only for duration measuring purposes.
previous_now: Instant::now(),
tick_offset: get_time(ClockType::Real) as i64,
@@ -334,12 +289,12 @@ impl RTC {
}
}
impl BusDevice for RTC {
impl BusDevice for Rtc {
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
let v;
let mut read_ok = true;
if offset < AMBA_ID_HIGH && offset >= AMBA_ID_LOW {
if (AMBA_ID_LOW..AMBA_ID_HIGH).contains(&offset) {
let index = ((offset - AMBA_ID_LOW) >> 2) as usize;
v = u32::from(PL031_ID[index]);
} else {
@@ -371,9 +326,9 @@ impl BusDevice for RTC {
}
}
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if data.len() <= 4 {
let v = read_le_u32(&data[..]);
let v = read_le_u32(&data);
if let Err(e) = self.handle_write(offset, v) {
warn!("Failed to write to RTC PL031 device: {}", e);
}
@@ -384,12 +339,18 @@ impl BusDevice for RTC {
data.len()
);
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u32, read_le_u64, write_be_u16,
write_be_u32, write_le_i32, write_le_u16, write_le_u32, write_le_u64,
};
use std::sync::Arc;
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
use vmm_sys_util::eventfd::EventFd;
@@ -474,8 +435,8 @@ mod tests {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<&EventFd> {
Some(&self.event_fd)
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap())
}
}
@@ -489,7 +450,7 @@ mod tests {
fn test_rtc_read_write_and_event() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut rtc = RTC::new(Arc::new(Box::new(TestInterrupt::new(
let mut rtc = Rtc::new(Arc::new(Box::new(TestInterrupt::new(
intr_evt.try_clone().unwrap(),
))));
let mut data = [0; 4];
@@ -498,7 +459,7 @@ mod tests {
write_le_u32(&mut data, 123);
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
let v = read_le_u32(&data[..]);
let v = read_le_u32(&data);
assert_eq!(v, 123);
// Read and write to the LR register.
@@ -510,7 +471,7 @@ mod tests {
assert!(rtc.previous_now > previous_now_before);
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCLR, &mut data);
let v_read = read_le_u32(&data[..]);
let v_read = read_le_u32(&data);
assert_eq!((v / NANOS_PER_SECOND) as u32, v_read);
// Read and write to IMSC register.
@@ -521,14 +482,14 @@ mod tests {
// The interrupt line should be on.
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() == 1);
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
let v = read_le_u32(&data[..]);
let v = read_le_u32(&data);
assert_eq!(non_zero & 1, v);
// Now test with 0.
write_le_u32(&mut data, 0);
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
let v = read_le_u32(&data[..]);
let v = read_le_u32(&data);
assert_eq!(0, v);
// Read and write to the ICR register.
@@ -536,10 +497,10 @@ mod tests {
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCICR, &mut data);
// The interrupt line should be on.
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() > 1);
let v_before = read_le_u32(&data[..]);
let v_before = read_le_u32(&data);
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCICR, &mut data);
let v = read_le_u32(&data[..]);
let v = read_le_u32(&data);
// ICR is a write only register. Data received should stay equal to data sent.
assert_eq!(v, v_before);
@@ -547,7 +508,7 @@ mod tests {
write_le_u32(&mut data, 0);
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
let v = read_le_u32(&data[..]);
let v = read_le_u32(&data);
assert_eq!(v, 1);
// Attempts to write beyond the writable space. Using here the space used to read

View File

@@ -7,7 +7,7 @@
use anyhow::anyhow;
use std::collections::VecDeque;
use std::sync::Arc;
use std::sync::{Arc, Barrier};
use std::{io, result};
use vm_device::interrupt::InterruptSourceGroup;
use vm_device::BusDevice;
@@ -275,12 +275,14 @@ impl BusDevice for Serial {
};
}
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if data.len() != 1 {
return;
return None;
}
if let Err(_e) = self.handle_write(offset as u8, data[0]) {}
self.handle_write(offset as u8, data[0]).ok();
None
}
}
@@ -354,6 +356,9 @@ mod tests {
) -> result::Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap())
}
}
impl TestInterrupt {
@@ -394,13 +399,13 @@ mod tests {
Box::new(serial_out.clone()),
);
serial.write(0, DATA as u64, &['x' as u8, 'y' as u8]);
serial.write(0, DATA as u64, &['a' as u8]);
serial.write(0, DATA as u64, &['b' as u8]);
serial.write(0, DATA as u64, &['c' as u8]);
serial.write(0, DATA as u64, &[b'x', b'y']);
serial.write(0, DATA as u64, &[b'a']);
serial.write(0, DATA as u64, &[b'b']);
serial.write(0, DATA as u64, &[b'c']);
assert_eq!(
serial_out.buf.lock().unwrap().as_slice(),
&['a' as u8, 'b' as u8, 'c' as u8]
&[b'a', b'b', b'c']
);
}
@@ -411,16 +416,14 @@ mod tests {
let mut serial = Serial::new_out(
String::from(SERIAL_NAME),
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
Box::new(serial_out.clone()),
Box::new(serial_out),
);
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
// counter doesn't change (for 0 it blocks)
assert!(intr_evt.write(1).is_ok());
serial.write(0, IER as u64, &[IER_RECV_BIT]);
serial
.queue_input_bytes(&['a' as u8, 'b' as u8, 'c' as u8])
.unwrap();
serial.queue_input_bytes(&[b'a', b'b', b'c']).unwrap();
assert_eq!(intr_evt.read().unwrap(), 2);
@@ -433,11 +436,11 @@ mod tests {
serial.read(0, LSR as u64, &mut data[..]);
assert_ne!(data[0] & LSR_DATA_BIT, 0);
serial.read(0, DATA as u64, &mut data[..]);
assert_eq!(data[0], 'a' as u8);
assert_eq!(data[0], b'a');
serial.read(0, DATA as u64, &mut data[..]);
assert_eq!(data[0], 'b' as u8);
assert_eq!(data[0], b'b');
serial.read(0, DATA as u64, &mut data[..]);
assert_eq!(data[0], 'c' as u8);
assert_eq!(data[0], b'c');
// check if reading from the largest u8 offset returns 0
serial.read(0, 0xff, &mut data[..]);
@@ -456,7 +459,7 @@ mod tests {
// counter doesn't change (for 0 it blocks)
assert!(intr_evt.write(1).is_ok());
serial.write(0, IER as u64, &[IER_THR_BIT]);
serial.write(0, DATA as u64, &['a' as u8]);
serial.write(0, DATA as u64, &[b'a']);
assert_eq!(intr_evt.read().unwrap(), 2);
let mut data = [0u8];
@@ -474,13 +477,13 @@ mod tests {
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
);
serial.write(0, LCR as u64, &[LCR_DLAB_BIT as u8]);
serial.write(0, DLAB_LOW as u64, &[0x12 as u8]);
serial.write(0, DLAB_HIGH as u64, &[0x34 as u8]);
serial.write(0, LCR as u64, &[LCR_DLAB_BIT]);
serial.write(0, DLAB_LOW as u64, &[0x12]);
serial.write(0, DLAB_HIGH as u64, &[0x34]);
let mut data = [0u8];
serial.read(0, LCR as u64, &mut data[..]);
assert_eq!(data[0], LCR_DLAB_BIT as u8);
assert_eq!(data[0], LCR_DLAB_BIT);
serial.read(0, DLAB_LOW as u64, &mut data[..]);
assert_eq!(data[0], 0x12);
serial.read(0, DLAB_HIGH as u64, &mut data[..]);
@@ -495,22 +498,22 @@ mod tests {
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
);
serial.write(0, MCR as u64, &[MCR_LOOP_BIT as u8]);
serial.write(0, DATA as u64, &['a' as u8]);
serial.write(0, DATA as u64, &['b' as u8]);
serial.write(0, DATA as u64, &['c' as u8]);
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
serial.write(0, DATA as u64, &[b'a']);
serial.write(0, DATA as u64, &[b'b']);
serial.write(0, DATA as u64, &[b'c']);
let mut data = [0u8];
serial.read(0, MSR as u64, &mut data[..]);
assert_eq!(data[0], DEFAULT_MODEM_STATUS as u8);
assert_eq!(data[0], DEFAULT_MODEM_STATUS);
serial.read(0, MCR as u64, &mut data[..]);
assert_eq!(data[0], MCR_LOOP_BIT as u8);
assert_eq!(data[0], MCR_LOOP_BIT);
serial.read(0, DATA as u64, &mut data[..]);
assert_eq!(data[0], 'a' as u8);
assert_eq!(data[0], b'a');
serial.read(0, DATA as u64, &mut data[..]);
assert_eq!(data[0], 'b' as u8);
assert_eq!(data[0], b'b');
serial.read(0, DATA as u64, &mut data[..]);
assert_eq!(data[0], 'c' as u8);
assert_eq!(data[0], b'c');
}
#[test]
@@ -521,10 +524,10 @@ mod tests {
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
);
serial.write(0, SCR as u64, &[0x12 as u8]);
serial.write(0, SCR as u64, &[0x12]);
let mut data = [0u8];
serial.read(0, SCR as u64, &mut data[..]);
assert_eq!(data[0], 0x12 as u8);
assert_eq!(data[0], 0x12);
}
}

View File

@@ -0,0 +1,506 @@
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! ARM PrimeCell UART(PL011)
//!
//! This module implements an ARM PrimeCell UART(PL011).
//!
use crate::{read_le_u32, write_le_u32};
use anyhow::anyhow;
use std::collections::VecDeque;
use std::fmt;
use std::sync::{Arc, Barrier};
use std::{io, result};
use vm_device::interrupt::InterruptSourceGroup;
use vm_device::BusDevice;
use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
Transportable,
};
/* Registers */
const UARTDR: u64 = 0;
const UARTRSR_UARTECR: u64 = 1;
const UARTFR: u64 = 6;
const UARTILPR: u64 = 8;
const UARTIBRD: u64 = 9;
const UARTFBRD: u64 = 10;
const UARTLCR_H: u64 = 11;
const UARTCR: u64 = 12;
const UARTIFLS: u64 = 13;
const UARTIMSC: u64 = 14;
const UARTRIS: u64 = 15;
const UARTMIS: u64 = 16;
const UARTICR: u64 = 17;
const UARTDMACR: u64 = 18;
const PL011_INT_TX: u32 = 0x20;
const PL011_INT_RX: u32 = 0x10;
const PL011_FLAG_RXFF: u32 = 0x40;
const PL011_FLAG_RXFE: u32 = 0x10;
const PL011_ID: [u8; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
// We are only interested in the margins.
const AMBA_ID_LOW: u64 = 0x3f8;
const AMBA_ID_HIGH: u64 = 0x401;
#[derive(Debug)]
pub enum Error {
BadWriteOffset(u64),
DmaNotImplemented,
InterruptFailure(io::Error),
WriteAllFailure(io::Error),
FlushFailure(io::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::BadWriteOffset(offset) => write!(f, "pl011_write: Bad Write Offset: {}", offset),
Error::DmaNotImplemented => write!(f, "pl011: DMA not implemented."),
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {}", e),
Error::WriteAllFailure(e) => write!(f, "Failed to write: {}", e),
Error::FlushFailure(e) => write!(f, "Failed to flush: {}", e),
}
}
}
type Result<T> = result::Result<T, Error>;
/// A PL011 device following the PL011 specification.
pub struct Pl011 {
id: String,
flags: u32,
lcr: u32,
rsr: u32,
cr: u32,
dmacr: u32,
int_enabled: u32,
int_level: u32,
read_fifo: VecDeque<u8>,
ilpr: u32,
ibrd: u32,
fbrd: u32,
ifl: u32,
read_count: u32,
read_trigger: u32,
irq: Arc<Box<dyn InterruptSourceGroup>>,
out: Option<Box<dyn io::Write + Send>>,
}
#[derive(Serialize, Deserialize)]
pub struct Pl011State {
flags: u32,
lcr: u32,
rsr: u32,
cr: u32,
dmacr: u32,
int_enabled: u32,
int_level: u32,
read_fifo: VecDeque<u8>,
ilpr: u32,
ibrd: u32,
fbrd: u32,
ifl: u32,
read_count: u32,
read_trigger: u32,
}
impl Pl011 {
/// Constructs an AMBA PL011 UART device.
pub fn new(
id: String,
irq: Arc<Box<dyn InterruptSourceGroup>>,
out: Option<Box<dyn io::Write + Send>>,
) -> Self {
Self {
id,
flags: 0x90u32,
lcr: 0u32,
rsr: 0u32,
cr: 0x300u32,
dmacr: 0u32,
int_enabled: 0u32,
int_level: 0u32,
read_fifo: VecDeque::new(),
ilpr: 0u32,
ibrd: 0u32,
fbrd: 0u32,
ifl: 0x12u32,
read_count: 0u32,
read_trigger: 1u32,
irq,
out,
}
}
fn state(&self) -> Pl011State {
Pl011State {
flags: self.flags,
lcr: self.lcr,
rsr: self.rsr,
cr: self.cr,
dmacr: self.dmacr,
int_enabled: self.int_enabled,
int_level: self.int_level,
read_fifo: self.read_fifo.clone(),
ilpr: self.ilpr,
ibrd: self.ibrd,
fbrd: self.fbrd,
ifl: self.ifl,
read_count: self.read_count,
read_trigger: self.read_trigger,
}
}
fn set_state(&mut self, state: &Pl011State) {
self.flags = state.flags;
self.lcr = state.lcr;
self.rsr = state.rsr;
self.cr = state.cr;
self.dmacr = state.dmacr;
self.int_enabled = state.int_enabled;
self.int_level = state.int_level;
self.read_fifo = state.read_fifo.clone();
self.ilpr = state.ilpr;
self.ibrd = state.ibrd;
self.fbrd = state.fbrd;
self.ifl = state.ifl;
self.read_count = state.read_count;
self.read_trigger = state.read_trigger;
}
/// Queues raw bytes for the guest to read and signals the interrupt
pub fn queue_input_bytes(&mut self, c: &[u8]) -> vmm_sys_util::errno::Result<()> {
self.read_fifo.extend(c);
self.read_count += c.len() as u32;
self.flags &= !PL011_FLAG_RXFE;
if ((self.lcr & 0x10) == 0) || (self.read_count == 16) {
self.flags |= PL011_FLAG_RXFF;
}
if self.read_count >= self.read_trigger {
self.int_level |= PL011_INT_RX;
self.trigger_interrupt()?;
}
Ok(())
}
fn pl011_get_baudrate(&self) -> u32 {
if self.fbrd == 0 {
return 0;
}
let clk = 24_000_000; // We set the APB_PLCK to 24M in device tree
(clk / ((self.ibrd << 6) + self.fbrd)) << 2
}
fn pl011_trace_baudrate_change(&self) {
debug!(
"=== New baudrate: {:#?} (clk: {:#?}Hz, ibrd: {:#?}, fbrd: {:#?}) ===",
self.pl011_get_baudrate(),
24_000_000, // We set the APB_PLCK to 24M in device tree
self.ibrd,
self.fbrd
);
}
fn pl011_set_read_trigger(&mut self) {
self.read_trigger = 1;
}
fn handle_write(&mut self, offset: u64, val: u32) -> Result<()> {
match offset >> 2 {
UARTDR => {
self.int_level |= PL011_INT_TX;
if let Some(out) = self.out.as_mut() {
out.write_all(&[val.to_le_bytes()[0]])
.map_err(Error::WriteAllFailure)?;
out.flush().map_err(Error::FlushFailure)?;
}
}
UARTRSR_UARTECR => {
self.rsr = 0;
}
UARTFR => { /* Writes to Flag register are ignored.*/ }
UARTILPR => {
self.ilpr = val;
}
UARTIBRD => {
self.ibrd = val;
self.pl011_trace_baudrate_change();
}
UARTFBRD => {
self.fbrd = val;
self.pl011_trace_baudrate_change();
}
UARTLCR_H => {
/* Reset the FIFO state on FIFO enable or disable */
if ((self.lcr ^ val) & 0x10) != 0 {
self.read_count = 0;
}
self.lcr = val;
self.pl011_set_read_trigger();
}
UARTCR => {
self.cr = val;
}
UARTIFLS => {
self.ifl = val;
self.pl011_set_read_trigger();
}
UARTIMSC => {
self.int_enabled = val;
self.trigger_interrupt().map_err(Error::InterruptFailure)?;
}
UARTICR => {
self.int_level &= !val;
self.trigger_interrupt().map_err(Error::InterruptFailure)?;
}
UARTDMACR => {
self.dmacr = val;
if (val & 3) != 0 {
return Err(Error::DmaNotImplemented);
}
}
off => {
return Err(Error::BadWriteOffset(off));
}
}
Ok(())
}
fn trigger_interrupt(&mut self) -> result::Result<(), io::Error> {
self.irq.trigger(0)
}
}
impl BusDevice for Pl011 {
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
let v;
let mut read_ok = true;
if (AMBA_ID_LOW..AMBA_ID_HIGH).contains(&(offset >> 2)) {
let index = ((offset - 0xfe0) >> 2) as usize;
v = u32::from(PL011_ID[index]);
} else {
v = match offset >> 2 {
UARTDR => {
let c: u32;
let r: u32;
self.flags &= !PL011_FLAG_RXFF;
c = self.read_fifo.pop_front().unwrap_or_default().into();
if self.read_count > 0 {
self.read_count -= 1;
}
if self.read_count == 0 {
self.flags |= PL011_FLAG_RXFE;
}
if self.read_count == (self.read_trigger - 1) {
self.int_level &= !PL011_INT_RX;
}
self.rsr = c >> 8;
r = c;
r
}
UARTRSR_UARTECR => self.rsr,
UARTFR => self.flags,
UARTILPR => self.ilpr,
UARTIBRD => self.ibrd,
UARTFBRD => self.fbrd,
UARTLCR_H => self.lcr,
UARTCR => self.cr,
UARTIFLS => self.ifl,
UARTIMSC => self.int_enabled,
UARTRIS => self.int_level,
UARTMIS => (self.int_level & self.int_enabled),
UARTDMACR => self.dmacr,
_ => {
read_ok = false;
0
}
}
}
if read_ok && data.len() <= 4 {
write_le_u32(data, v);
} else {
warn!(
"Invalid PL011 read: offset {}, data length {}",
offset,
data.len()
);
}
}
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if data.len() <= 4 {
let v = read_le_u32(&data);
if let Err(e) = self.handle_write(offset, v) {
warn!("Failed to write to PL011 device: {}", e);
}
} else {
warn!(
"Invalid PL011 write: offset {}, data length {}",
offset,
data.len()
);
}
None
}
}
impl Snapshottable for Pl011 {
fn id(&self) -> String {
self.id.clone()
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
let snapshot =
serde_json::to_vec(&self.state()).map_err(|e| MigratableError::Snapshot(e.into()))?;
let mut pl011_snapshot = Snapshot::new(self.id.as_str());
pl011_snapshot.add_data_section(SnapshotDataSection {
id: format!("{}-section", self.id),
snapshot,
});
Ok(pl011_snapshot)
}
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
if let Some(pl011_section) = snapshot.snapshot_data.get(&format!("{}-section", self.id)) {
let pl011_state = match serde_json::from_slice(&pl011_section.snapshot) {
Ok(state) => state,
Err(error) => {
return Err(MigratableError::Restore(anyhow!(
"Could not deserialize PL011 {}",
error
)))
}
};
self.set_state(&pl011_state);
return Ok(());
}
Err(MigratableError::Restore(anyhow!(
"Could not find the PL011 snapshot section"
)))
}
}
impl Pausable for Pl011 {}
impl Transportable for Pl011 {}
impl Migratable for Pl011 {}
#[cfg(test)]
mod tests {
use super::*;
use std::io;
use std::sync::{Arc, Mutex};
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
use vmm_sys_util::eventfd::EventFd;
const SERIAL_NAME: &str = "serial";
struct TestInterrupt {
event_fd: EventFd,
}
impl InterruptSourceGroup for TestInterrupt {
fn trigger(&self, _index: InterruptIndex) -> result::Result<(), std::io::Error> {
self.event_fd.write(1)
}
fn update(
&self,
_index: InterruptIndex,
_config: InterruptSourceConfig,
) -> result::Result<(), std::io::Error> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
Some(self.event_fd.try_clone().unwrap())
}
}
impl TestInterrupt {
fn new(event_fd: EventFd) -> Self {
TestInterrupt { event_fd }
}
}
#[derive(Clone)]
struct SharedBuffer {
buf: Arc<Mutex<Vec<u8>>>,
}
impl SharedBuffer {
fn new() -> SharedBuffer {
SharedBuffer {
buf: Arc::new(Mutex::new(Vec::new())),
}
}
}
impl io::Write for SharedBuffer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.lock().unwrap().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.buf.lock().unwrap().flush()
}
}
#[test]
fn pl011_output() {
let intr_evt = EventFd::new(0).unwrap();
let pl011_out = SharedBuffer::new();
let mut pl011 = Pl011::new(
String::from(SERIAL_NAME),
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
Some(Box::new(pl011_out.clone())),
);
pl011.write(0, UARTDR as u64, &[b'x', b'y']);
pl011.write(0, UARTDR as u64, &[b'a']);
pl011.write(0, UARTDR as u64, &[b'b']);
pl011.write(0, UARTDR as u64, &[b'c']);
assert_eq!(
pl011_out.buf.lock().unwrap().as_slice(),
&[b'x', b'a', b'b', b'c']
);
}
#[test]
fn pl011_input() {
let intr_evt = EventFd::new(0).unwrap();
let pl011_out = SharedBuffer::new();
let mut pl011 = Pl011::new(
String::from(SERIAL_NAME),
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
Some(Box::new(pl011_out)),
);
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
// counter doesn't change (for 0 it blocks)
assert!(intr_evt.write(1).is_ok());
pl011.queue_input_bytes(&[b'a', b'b', b'c']).unwrap();
assert_eq!(intr_evt.read().unwrap(), 2);
let mut data = [0u8];
pl011.read(0, UARTDR as u64, &mut data);
assert_eq!(data[0], b'a');
pl011.read(0, UARTDR as u64, &mut data);
assert_eq!(data[0], b'b');
pl011.read(0, UARTDR as u64, &mut data);
assert_eq!(data[0], b'c');
}
}

View File

@@ -26,7 +26,7 @@ extern crate serde_derive;
extern crate serde_json;
#[cfg(feature = "acpi")]
mod acpi;
pub mod acpi;
#[cfg(target_arch = "aarch64")]
pub mod gic;
pub mod interrupt_controller;
@@ -35,13 +35,74 @@ pub mod ioapic;
pub mod legacy;
#[cfg(feature = "acpi")]
pub use self::acpi::{AcpiGEDDevice, AcpiPMTimerDevice, AcpiShutdownDevice};
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
bitflags! {
pub struct HotPlugNotificationFlags: u8 {
pub struct AcpiNotificationFlags: u8 {
const NO_DEVICES_CHANGED = 0;
const CPU_DEVICES_CHANGED = 0b1;
const MEMORY_DEVICES_CHANGED = 0b10;
const PCI_DEVICES_CHANGED = 0b100;
const POWER_BUTTON_CHANGED = 0b1000;
}
}
#[allow(unused_macros)]
#[cfg(target_arch = "aarch64")]
macro_rules! generate_read_fn {
($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => {
#[allow(dead_code)]
pub fn $fn_name(input: &[$byte_type]) -> $data_type {
assert!($type_size == std::mem::size_of::<$data_type>());
let mut array = [0u8; $type_size];
for (byte, read) in array.iter_mut().zip(input.iter().cloned()) {
*byte = read as u8;
}
<$data_type>::$endian_type(array)
}
};
}
#[allow(unused_macros)]
#[cfg(target_arch = "aarch64")]
macro_rules! generate_write_fn {
($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => {
#[allow(dead_code)]
pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) {
for (byte, read) in buf
.iter_mut()
.zip(<$data_type>::$endian_type(n).iter().cloned())
{
*byte = read as $byte_type;
}
}
};
}
#[cfg(target_arch = "aarch64")]
generate_read_fn!(read_le_u16, u16, u8, 2, from_le_bytes);
#[cfg(target_arch = "aarch64")]
generate_read_fn!(read_le_u32, u32, u8, 4, from_le_bytes);
#[cfg(target_arch = "aarch64")]
generate_read_fn!(read_le_u64, u64, u8, 8, from_le_bytes);
#[cfg(target_arch = "aarch64")]
generate_read_fn!(read_le_i32, i32, i8, 4, from_le_bytes);
#[cfg(target_arch = "aarch64")]
generate_read_fn!(read_be_u16, u16, u8, 2, from_be_bytes);
#[cfg(target_arch = "aarch64")]
generate_read_fn!(read_be_u32, u32, u8, 4, from_be_bytes);
#[cfg(target_arch = "aarch64")]
generate_write_fn!(write_le_u16, u16, u8, to_le_bytes);
#[cfg(target_arch = "aarch64")]
generate_write_fn!(write_le_u32, u32, u8, to_le_bytes);
#[cfg(target_arch = "aarch64")]
generate_write_fn!(write_le_u64, u64, u8, to_le_bytes);
#[cfg(target_arch = "aarch64")]
generate_write_fn!(write_le_i32, i32, i8, to_le_bytes);
#[cfg(target_arch = "aarch64")]
generate_write_fn!(write_be_u16, u16, u8, to_be_bytes);
#[cfg(target_arch = "aarch64")]
generate_write_fn!(write_be_u32, u32, u8, to_be_bytes);

View File

@@ -81,16 +81,24 @@ apt update
apt install fio iperf iperf3 socat
```
### Remove snapd
### Remove counterproductive packages
* snapd:
This prevents snapd from trying to mount squashfs filesystem when the kernel
might not support it. This might be the case when the image is used with direct
kernel boot. This step is specific to Ubuntu distributions.
* pollinate:
Remove this package which can fail and lead to the SSH daemon failing to start.
See #2113 for details.
```bash
apt remove --purge snapd
apt remove --purge snapd pollinate
```
### Cleanup the image
Leave no trace in the image before unmounting its content.
@@ -108,15 +116,18 @@ umount /mnt
Renaming is important to identify this is a modified image.
```bash
mv focal-server-cloudimg-amd64.raw focal-server-cloudimg-amd64-custom.raw
mv focal-server-cloudimg-amd64.raw focal-server-cloudimg-amd64-custom-$(date "+%Y%m%d")-0.raw
```
The `-0` is the revision and is only necessary to change if multiple images are
updated on the same day.
### Create QCOW2 from RAW
Last step is to create the QCOW2 image back from the modified image.
```bash
qemu-img convert -p -f raw -O qcow2 focal-server-cloudimg-amd64-custom.raw focal-server-cloudimg-amd64-custom.qcow2
qemu-img convert -p -f raw -O qcow2 focal-server-cloudimg-amd64-custom-$(date "+%Y%m%d")-0.raw focal-server-cloudimg-amd64-custom-$(date "+%Y%m%d")-0.qcow2
```
## Switch CI to use the new image
@@ -134,3 +145,5 @@ Last step is about updating the integration tests to work with this new image.
The key point is to identify where the Linux filesystem partition is located,
as we might need to update the direct kernel boot command line, replacing
`/dev/vda1` with the appropriate partition number.
Update all references to the previous image name to the new one.

View File

@@ -28,9 +28,13 @@ This document describes the device model supported by `cloud-hypervisor`.
### Serial port
Simple emulation of a serial port by reading and writing to specific port I/O
addresses. Used as the default console for Linux when booting with the option
`console=ttyS0`, the serial port can be very useful to gather early logs from
the operating system booted inside the VM.
addresses. The serial port can be very useful to gather early logs from the
operating system booted inside the VM.
For x86_64, The default serial port is from an emulated 16550A device. It can
be used as the default console for Linux when booting with the option
`console=ttyS0`. For AArch64, the default serial port is from an emulated
PL011 UART device. The related command line for AArch64 is `console=ttyAMA0`.
This device is always built-in, and it is disabled by default. It can be
enabled with the `--serial` option, as long as its parameter is not `off`.
@@ -44,6 +48,10 @@ This device is built-in by default, but it can be compiled out with Rust
features. When compiled in, it is always enabled, and cannot be disabled
from the command line.
For AArch64 machines, an ARM PrimeCell Real Time Clock(PL031) is implemented.
This device is built-in by default for the AArch64 platform, and it is always
enabled, and cannot be disabled from the command line.
### I/O APIC
`cloud-hypervisor` supports a so-called split IRQ chip implementation by
@@ -65,6 +73,11 @@ enabled by default, the handling of reboot/shutdown goes through the dedicated
ACPI device. In case ACPI is disabled, this device is enabled to bring to the
VM some reboot/shutdown support.
### ARM PrimeCell General Purpose Input/Output (PL061)
Simplified ARM PrimeCell GPIO (PL061) implementation. Only supports key 3 to
trigger a graceful shutdown of the AArch64 guest.
### ACPI device
This is a dedicated device for handling ACPI shutdown and reboot when ACPI is

View File

@@ -27,7 +27,7 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
--rng \
--api-socket=/tmp/ch-socket
--api-socket=/tmp/ch-socket
$ popd
```
@@ -36,7 +36,7 @@ Notice the addition of `--api-socket=/tmp/ch-socket` and a `max` parameter on `-
To ask the VMM to add additional vCPUs then use the resize API:
```shell
curl -H "Accept: application/json" -H "Content-Type: application/json" -i -XPUT --unix-socket /tmp/ch-socket -d "{ \"desired_vcpus\":8}" http://localhost/api/v1/vm.resize
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 8
```
The extra vCPU threads will be created and advertised to the running kernel. The kernel does not bring up the CPUs immediately and instead the user must "online" them from inside the VM:
@@ -56,19 +56,23 @@ After a reboot the added CPUs will remain.
Removing CPUs works similarly by reducing the number in the "desired_vcpus" field of the reisze API. The CPUs will be automatically offlined inside the guest so there is no need to run any commands inside the guest:
```shell
curl -H "Accept: application/json" -H "Content-Type: application/json" -i -XPUT --unix-socket /tmp/ch-socket -d "{ \"desired_vcpus\":2}" http://localhost/api/v1/vm.resize
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 2
```
As per adding CPUs to the guest, after a reboot the VM will be running with the reduced number of vCPUs.
## Memory Hot Plug
### ACPI method
Extra memory can be added from a running Cloud Hypervisor instance. This is controlled by two mechanisms:
1. Allocating some of the guest physical address space for hotplug memory.
2. Making a HTTP API request to the VMM to ask for a new amount of RAM to be assigned to the VM. In the case of expanding the memory for the VM the new memory will be hotplugged into the running VM, if reducing the size of the memory then change will take effect after the next reboot.
To use memory hotplug start the VM specifying some size RAM in the "hotplug_size" parameter to the memory configuration. Not all the memory specified in this parameter will be available to hotplug as there are spacing and alignment requirements so it is recommended to make it larger than the hotplug RAM needed.
To use memory hotplug start the VM specifying some size RAM in the `hotplug_size` parameter to the memory configuration. Not all the memory specified in this parameter will be available to hotplug as there are spacing and alignment requirements so it is recommended to make it larger than the hotplug RAM needed.
Because the ACPI method is the default, there is no need to add the extra option `hotplug_method=acpi`.
```shell
$ pushd $CLOUDH
@@ -81,7 +85,7 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
--memory size=1024M,hotplug_size=8192M \
--net "tap=,mac=,ip=,mask=" \
--rng \
--api-socket=/tmp/ch-socket
--api-socket=/tmp/ch-socket
$ popd
```
@@ -91,10 +95,10 @@ Before issuing the API request it is necessary to run the following command insi
root@ch-guest ~ # echo online | sudo tee /sys/devices/system/memory/auto_online_blocks
```
To ask the VMM to add expand the RAM for the VM (request is in bytes):
To ask the VMM to expand the RAM for the VM:
```shell
curl -H "Accept: application/json" -H "Content-Type: application/json" -i -XPUT --unix-socket /tmp/ch-socket -d "{ \"desired_vcpus\": 4, \"desired_ram\" : 3221225472}" http://localhost/api/v1/vm.resize
./ch-remote --api-socket=/tmp/ch-socket resize --memory 3G
```
The new memory is now available to use inside the VM:
@@ -111,3 +115,134 @@ Due to guest OS limitations is is necessary to ensure that amount of memory adde
The same API can also be used to reduce the desired RAM for a VM but the change will not be applied until the VM is rebooted.
Memory and CPU resizing can be combined together into the same HTTP API request.
### virtio-mem method
Extra memory can be added and removed from a running Cloud Hypervisor instance. This is controlled by two mechanisms:
1. Allocating some of the guest physical address space for hotplug memory.
2. Making a HTTP API request to the VMM to ask for a new amount of RAM to be assigned to the VM.
To use memory hotplug start the VM specifying some size RAM in the `hotplug_size` parameter along with `hotplug_method=virtio-mem` to the memory configuration.
```shell
$ pushd $CLOUDH
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
--kernel custom-vmlinux.bin \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--disk path=focal-server-cloudimg-amd64.raw \
--memory size=1024M,hotplug_size=8192M,hotplug_method=virtio-mem \
--net "tap=,mac=,ip=,mask=" \
--api-socket=/tmp/ch-socket
$ popd
```
To ask the VMM to expand the RAM for the VM (request is in bytes):
```shell
./ch-remote --api-socket=/tmp/ch-socket resize --memory 3G
```
The new memory is now available to use inside the VM:
```shell
free -h
total used free shared buff/cache available
Mem: 3.0Gi 71Mi 2.8Gi 0.0Ki 47Mi 2.8Gi
Swap: 32Mi 0B 32Mi
```
The same API can also be used to reduce the desired RAM for a VM. It is important to note that reducing RAM size might only partially work, as the guest might be using some of it.
## PCI Device Hot Plug
Extra PCI devices can be added and removed from a running Cloud Hypervisor instance. This is controlled by making a HTTP API request to the VMM to ask for the additional device to be added, or for the existing device to be removed.
To use PCI device hotplug start the VM with the HTTP server.
```shell
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
--kernel custom-vmlinux.bin \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--disk path=focal-server-cloudimg-amd64.raw \
--cpus boot=4 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
--api-socket=/tmp/ch-socket
```
Notice the addition of `--api-socket=/tmp/ch-socket`.
### Add VFIO Device
To ask the VMM to add additional VFIO device then use the `add-device` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-device path=/sys/bus/pci/devices/0000:01:00.0/
```
### Add Disk Device
To ask the VMM to add additional disk device then use the `add-disk` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-disk path=/foo/bar/cloud.img
```
### Add Fs Device
To ask the VMM to add additional fs device then use the `add-fs` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-fs tag=myfs,socket=/foo/bar/virtiofs.sock
```
### Add Net Device
To ask the VMM to add additional network device then use the `add-net` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-net tap=chtap0
```
### Add Pmem Device
To ask the VMM to add additional PMEM device then use the `add-pmem` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-pmem file=/foo/bar.cloud.img
```
### Add Vsock Device
To ask the VMM to add additional vsock device then use the `add-vsock` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-vsock cid=3,socket=/foo/bar/vsock.sock
```
### Common Across All PCI Devices
The extra PCI device will be created and advertised to the running kernel. The new device can be found by checking the list of PCI devices.
```shell
root@ch-guest ~ # lspci
00:00.0 Host bridge: Intel Corporation Device 0d57
00:01.0 Unassigned class [ffff]: Red Hat, Inc. Virtio console (rev 01)
00:02.0 Mass storage controller: Red Hat, Inc. Virtio block device (rev 01)
00:03.0 Unassigned class [ffff]: Red Hat, Inc. Virtio RNG (rev 01)
```
After a reboot the added PCI device will remain.
### Remove PCI device
Removing a PCI device works the same way for all kind of PCI devices. The unique identifier related to the device must be provided. This identifier can be provided by the user when adding the new device, or by default Cloud Hypervisor will assign one.
```shell
./ch-remote --api-socket=/tmp/ch-socket remove-device _disk0
```
As per adding a PCI device to the guest, after a reboot the VM will be running without the removed PCI device.

View File

@@ -8,7 +8,7 @@ the host kernel. The required Linux and KVM changes can be found in the
[KVM SGX Tree](https://github.com/intel/kvm-sgx).
Utilizing SGX in the guest requires a kernel/OS with SGX support, e.g. a kernel
built using the [SGX Linux Development Tree](https://github.com/jsakkine-intel/linux-sgx.git)
built using the [SGX Linux Development Tree](https://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-sgx.git)
or the [KVM SGX Tree](https://github.com/intel/kvm-sgx). Running KVM SGX as the
guest kernel allows nested virtualization of SGX.
@@ -19,7 +19,7 @@ following [instructions](https://github.com/intel/linux-sgx).
## Cloud-Hypervisor support
Assuming the host exposes `/dev/sgx/virt_epc`, we can pass SGX enclaves through
Assuming the host exposes `/dev/sgx_virt_epc`, we can pass SGX enclaves through
the guest.
In order to use SGX enclaves within a Cloud-Hypervisor VM, we must define one
@@ -42,12 +42,12 @@ Once booted, and assuming your guest kernel contains the patches from the
have been correctly created under `/dev/sgx`:
```bash
ls /dev/sgx
enclave provision virt_epc
ls /dev/sgx*
/dev/sgx_enclave /dev/sgx_provision /dev/sgx_virt_epc
```
From this point, it is possible to run any SGX application from the guest, as
it will access `/dev/sgx/enclave` device to create dedicated SGX enclaves.
it will access `/dev/sgx_enclave` device to create dedicated SGX enclaves.
Note: There is only one contiguous SGX EPC region, which contains all SGX EPC
sections. This region is exposed through ACPI and marked as reserved through

41
docs/io_throttling.md Normal file
View File

@@ -0,0 +1,41 @@
# I/O Throttling
Cloud Hypervisor now supports I/O throttling on virtio-block
devices. This support is based on the [`rate-limiter` module](https://github.com/firecracker-microvm/firecracker/tree/master/src/rate_limiter)
from Firecracker. This document explains the user interface of this
feature, and highlights some internal implementations that can help users
better understand the expected behavior of I/O throttling in practice.
Cloud Hypervisor allows to limit both the I/O bandwidth (e.g. bytes/s)
and I/O operations (ops/s) independently. To limit the I/O bandwidth, it
provides three user options, i.e., `bw_size` (bytes), `bw_one_time_burst`
(bytes), and `bw_refill_time` (ms). Both `bw_size` and `bw_refill_time`
are required, while `bw_one_time_burst` is optional.
Internally, these options define a TokenBucket with a maximum capacity
(`bw_size` bytes), an initial burst size (`bw_one_time_burst`) and an
interval for refilling purposes (`bw_refill_time`). The "refill-rate" is
`bw_size` bytes per `bw_refill_time` ms, and it is the constant rate at
which the tokens replenish. The refill process only starts happening
after the initial burst budget is consumed. Consumption from the token
bucket is unbounded in speed which allows for bursts bound in size by
the amount of tokens available. Once the token bucket is empty,
consumption speed is bound by the "refill-rate". Similarly, Cloud
Hypervisor provides another three options for limiting I/O operations,
i.e., `ops_size` (I/O operations), `bw_one_time_burst` (I/O operations),
and `bw_refill_time` (ms).
One caveat in the I/O throttling is that every-time the bucket gets
empty, it will stop I/O operations for a fixed amount of time
(`cool_down_time`). The `cool_down_time` now is fixed at `100 ms`, it
can have big implications to the actual rate limit (which can be a lot
different the expected "refill-rate" derived from user inputs). For
example, to have a 1000 IOPS limit, users should be able to provide
either of the following two options:
`ops_size=1000,ops_refill_time=1000` or
`ops_size=10,ops_refill_time=10`. However, the actual IOPS limits are
likely to be ~1000 IOPS and ~100 IOPS respectively. The reason is the
actual rate limit users get can be as low as
`ops_size/(ops_refill_time+cool_down_time)`. As a result, it is
generally advisable to keep `bw/ops_refill_time` larger than `100 ms`
(`cool_down_time`) to make sure the actual rate limit is close to users'
expectation ("refill-rate").

32
docs/macvtap-bridge.md Normal file
View File

@@ -0,0 +1,32 @@
# Using MACVTAP to Bridge onto Host Network
Cloud Hypervisor supports using a MACVTAP device which is derived from a MACVLAN. Full details of configuring MACVLAN or MACVTAP is out of scope of this document. However the example below indicates how to bridge the guest directly onto the the network the host is on. Due to the lack of hairpin mode it not usually possible to reach the guest directly from the host.
```bash
# The MAC address must be attached to the macvtap and be used inside the guest
mac="c2:67:4f:53:29:cb"
# Host network adapter to bridge the guest onto
host_net="eno1"
# Create the macvtap0 as a new virtual MAC associated with the host network
sudo ip link add link "$host_net" name macvtap0 type macvtap
sudo ip link set macvtap0 address "$mac" up
sudo ip link show macvtap0
# A new character device is created for this interface
tapindex=$(< /sys/class/net/macvtap0/ifindex)
tapdevice="/dev/tap$tapindex"
# Ensure that we can access this device
sudo chown "$UID.$UID" "$tapdevice"
# Use --net fd=3 to point to fd 3 which the shell has opened to point to the /dev/tapN device
target/debug/cloud-hypervisor \
--kernel ~/src/linux/vmlinux \
--disk path=~/workloads/focal.raw \
--cpus boot=1 --memory size=512M \
--cmdline "root=/dev/vda1 console=hvc0" \
--net fd=3,mac=$mac 3<>$"$tapdevice"
```
As the guest is now connected to the same L2 network as the host you can obtain an IP address based on your host network (potentially including via DHCP)

View File

@@ -15,6 +15,7 @@ struct MemoryConfig {
mergeable: bool,
shared: bool,
hugepages: bool,
hugepage_size: Option<u64>,
hotplug_method: HotplugMethod,
hotplug_size: Option<u64>,
hotplugged_size: Option<u64>,
@@ -76,23 +77,24 @@ _Example_
--memory size=1G,shared=on
```
### `hugepages`
### `hugepages` and `hugepage_size`
Specifies if the memory must be `mmap(2)` with `MAP_HUGETLB` and `MAP_HUGE_2MB`
flags. This performs a memory mapping relying on 2MiB pages instead of the
default 4kiB pages.
Specifies if the memory must be created and `mmap(2)` with `MAP_HUGETLB` and size
flags. This performs a memory mapping relying on the specified huge page size. If no huge page size is supplied the system's default huge page size is used.
By using hugepages, one can improve the overall performance of the VM, assuming
the guest will allocate hugepages as well. Another interesting use case is VFIO
as it speeds up the VM's boot time since the amount of IOMMU mappings are
reduced.
The user is responsible for ensuring there are sufficient huge pages of the specified size for the VMM to use. Failure to do so may result in strange VMM behaviour.
By default this option is turned off.
_Example_
```
--memory size=1G,hugepages=on
--memory size=1G,hugepages=on,hugepage_size=2M
```
### `hotplug_method`

45
docs/profiling.md Normal file
View File

@@ -0,0 +1,45 @@
# Profiling
`perf` can be used to profile the `cloud-hypervisor` binary but it is necessary to make some modifications to the the build in order to produce a binary that gives useful results.
## Building a suitable binary
Modify the `Cargo.toml` file to add `debug = 1` to the `[profile.release]` block. It should look like this:
```
[profile.release]
lto = true
debug = 1
```
This adds the symbol information to the release binary but does not otherwise affect the performance.
The binary must also be built with frame pointers included so that the call graph can be captured by the profiler.
```
$ cargo clean && RUSTFLAGS='-C force-frame-pointers=y' cargo build --release
```
## Profiling
`perf` may then be used in the usual manner:
e.g.
```
$ perf record -g target/release/cloud-hypervisor \
--kernel ~/src/linux/vmlinux \
--pmem file=~/workloads/focal.raw \
--cpus boot=1 --memory size=1G \
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
--serial tty --console off \
--api-socket=/tmp/api1
```
For analysing the samples:
```
$ perf report -g
```
If profiling with a network device attached either the TAP device must be already created and configured or the profiling must be done as root so that the TAP device can be created.

68
docs/seccomp.md Normal file
View File

@@ -0,0 +1,68 @@
# Seccomp filtering
As a means to harden Cloud Hypervisor's security, the project leverages seccomp
filtering.
## What is seccomp filtering
A seccomp filter is a way for a process to tell the kernel which system calls
are authorized.
In case this process calls into a prohibited system call, the kernel will kill
the process right away.
## How does it apply to Cloud Hypervisor
Cloud Hypervisor is a multi threaded application. It spawns dedicated threads
for virtual CPUs, virtio devices and HTTP server, along with the main thread
representing the VMM.
Each of these threads has a limited scope of what it is expected to perform,
which is why different filters are applied to each of them.
By default, Cloud Hypervisor enables seccomp filtering as the project believes
that security should not be an option.
For development and debugging purposes, one might want to disable this feature
or log the faulty system call.
### Disabling seccomp filters
Append `--seccomp false` to Cloud Hypervisor's command line to prevent seccomp
filtering from being applied.
### Logging prohibited system calls
In the context of debug, one alternative to disabling seccomp filtering is to
log faulty system calls that would have caused the application to be killed by
the kernel.
Append `--seccomp log` to Cloud Hypervisor's command line to enable faulty
system calls to be logged.
The kernel running on the host machine must have the `audit` parameter enabled.
If this is not the case, update kernel boot options by appending `audit=1`.
Unauthorized system calls will be logged to the journal similarly to the
following example
```
type=SECCOMP msg=audit(1423263412.694:7878): auid=1000 uid=1000 gid=1000 ses=3 subj=unconfined_u:unconfined_r:cloud_hypervisor:s0-s0:c0.c1023 pid=1193 comm="cloud-hypervisor" exe="/usr/bin/cloud-hypervisor" sig=0 arch=c000003e syscall=47 compat=0 ip=0x7f4f63982604 code=0x50000
```
Provided `ausyscall` has been installed on the host, the system call can be
identified with
```
$ ausyscall 47
recvmsg
```
### Further debug with `strace`
One more way of debugging seccomp related issues is to use the `strace` tool as
it will log every system call issued by the process. It is important to use
`-f` option in order to trace each and every thread belonging to the process.
```
strace -f ./cloud-hypervisor ...
```

View File

@@ -35,8 +35,41 @@ To make Cloud Hypervisor use UEFI boot, pass the `OVMF.fd` file path as an argum
The same firmware can be used with Cloud Hypervisor or with QEMU. This is particularly useful if using QEMU for the preparation phase.
## Building UEFI Firmware with Compatibility Support Module (CSM)
CSM is a module that allows to boot legacy operating systems using the OVMF firmware. OVMF can embed a CSM build of SeaBIOS. To build the SeaBIOS with CSM support, add `CONFIG_CSM=y` to `.config` before the build. The outcome `out/Csm16.bin` is to be moved into `OvmfPkg/Csm/Csm16/Csm16.bin` before OVMF is built. Then, the OVMF build will have to be passed the `-D CSM_ENABLE` option in order to generate a legacy aware UEFI firmware. At the current stage, all the necessary patches are included in the Cloud Hypervisor specific [SeaBIOS branch](https://github.com/cloud-hypervisor/seabios/tree/ch). Taking into account the previous instructions, the modified command sequence to compile an OVMF binary with CSM support is the following one:
```shell
sudo apt-get update
sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git
git checkout https://github.com/cloud-hypervisor/seabios -b ch
cd seabios
make menuconfig
# Enable `CONFIG_CSM` and `CONFIG_QEMU_HARDWARE`
make CONFIG_CSM=y CONFIG_QEMU_HARDWARE=y
cd ..
git clone https://github.com/cloud-hypervisor/edk2 -b ch
cd edk2
. edksetup.sh
git submodule update --init
cp ../seabios/out/Csm16.bin OvmfPkg/Csm/Csm16/
echo "ACTIVE_PLATFORM=OvmfPkg/OvmfCh.dsc" >> Conf/target.txt
echo "TARGET_ARCH=X64" >> Conf/target.txt
echo "TOOL_CHAIN_TAG=GCC5" >> Conf/target.txt
make -C ./BaseTools
build
```
Please note, that the CSM support has currently only been tested with Linux guests. There are no plans to provide legacy support for other OSes (e.g. Windows).
# Links
- [OVMF wiki](https://github.com/tianocore/tianocore.github.io/wiki/OVMF)
- [Cloud Hypervisor specific tree](https://github.com/cloud-hypervisor/edk2/tree/ch)
- [Redhat OVMF Status Report](https://access.redhat.com/sites/default/files/attachments/ovmf-whtepaper-031815.pdf)
- [SeaBIOS Build Overview](https://www.seabios.org/Build_overview#Build_as_a_UEFI_Compatibility_Support_Module_.28CSM.29)

View File

@@ -78,5 +78,3 @@ takes the device's sysfs path as an argument. In our example it is
The guest kernel will then detect the card reader on its PCI bus and provided
that support for this device is enabled, it will probe and enable it for the
guest to use.

92
docs/virtiofs-root.md Normal file
View File

@@ -0,0 +1,92 @@
# HOWTO VirtioFS rootfs
A quick guide for using virtiofs as a cloud-hypervisor guest's rootfs (i.e.
with no root block device). This document is a quick getting started guide.
There are many more steps to take to make this a production ready, secure
setup.
## Prerequisites
1. virtiofsd from the qemu project
* We are using the Qemu version for now
* There is a Rust version being worked on that may be a better option in the future
* Part of the qemu-system-common package on Ubuntu
* Part of the qemu-common package on Fedora
2. cloud-hypervisor - the newer the better, but I tested with 0.12
3. a rootfs - This howto uses an alpine rootfs available here:
* https://dl-cdn.alpinelinux.org/alpine/v3.13/releases/x86_64/alpine-minirootfs-3.13.2-x86_64.tar.gz
* Others should work
## To create the VM rootfs
```bash
mkdir rootfs/
cd rootfs
# this needs sudo to be able to set root permissions on fs components
sudo tar -xf /path/to/alpine-minirootfs-3.13.1-x86_64.tar.gz
# this will get created when the VM actually boots by the dhcp client
# but we need it in the chroot to download packages
sudo cp /etc/resolv.conf etc/
# the alpine mini rootfs is meant for docker containers, we need a few extra
# things for a working rootfs
sudo chroot $PWD apk add openrc busybox-initscripts
# we are using the paravirt console in cloud-hypervisor, so enable it in init
# append it after the other console since it doesn't work just appending it
sudo sed -i '/vt100/a \n# paravirt console\nhvc0::respawn:/sbin/getty -L hvc0 115200 vt100' etc/inittab
# set no password for root user... you obviously don't want to do this for
# any sort of production setup
sudo sed -i 's/root:!::0:::::/root:::0:::::/' etc/shadow
# set up init scripts
for i in acpid crond
sudo ln -sf /etc/init.d/$i etc/runlevels/default/$i
end
for i in bootmisc hostname hwclock loadkmap modules networking swap sysctl syslog urandom
sudo ln -sf /etc/init.d/$i etc/runlevels/boot/$i
end
for i in killprocs mount-ro savecache
sudo ln -sf /etc/init.d/$i etc/runlevels/shutdown/$i
end
for i in devfs dmesg hwdrivers mdev
sudo ln -sf /etc/init.d/$i etc/runlevels/sysinit/$i
end
# setup network config
echo 'auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
' | sudo tee etc/network/interfaces
```
## To run the VM
```bash
# starting in the directory above rootfs
sudo virtiofsd --socket-path=$PWD/virtiofs-rootfs.sock -o source=$PWD/rootfs -o cache=none &
sudo cloud-hypervisor \
--cpus boot=1,max=1 \
--kernel vmlinux \
--fs tag=/dev/root,socket=$PWD/virtiofs-rootfs.sock \
--memory size=2G,shared=on \
--cmdline "console=hvc0 rootfstype=virtiofs root=/dev/root ro debug" \
--api-socket $PWD/ch.sock \
--rng \
--net ...
```
Note: an important part of the above is the `tag=/dev/root` and
`root=/dev/root` parts. For whatever reason, it would only work with that as
the tag.
Note: another important bit is that the memory is shared. This is required for
virtiofs
## Message from the author
If you find any issues or have suggestions, feel free to reach out to @iggy on
the cloud-hypervisor slack. Also if this works for you, I'd like to know as
well. It would also be nice to get steps for preparing other distribution root
filesystems.

View File

@@ -282,6 +282,17 @@ Note, this VM has the networking enabled. It is needed, because symbols and sour
Also, notice the `-serial` parameter - that's what does the magic on exposing the serial port to the guest while connecting the debugger VM with a client VM through the network. SAC/EMS needs to be disabled in the debugger VM, as otherwise the COM device might be blocked.
Hereafter, WinDbg can be started using a command below:
```cmd
set _NT_DEBUG_PORT=com1
set _NT_DEBUG_BAUD_RATE=115200
windbg -v -d -k
```
Once started, WinDbg will wait for an incoming connection which is going to be initialized by the debuggee VM started in the next section.
#### Invoke the Debuggee VM
##### Under QEMU

11
event_monitor/Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "event_monitor"
version = "0.1.0"
authors = ["The Cloud Hypervisor Authors"]
edition = "2018"
[dependencies]
libc = "0.2.91"
serde = {version = ">=1.0.27", features = ["rc"] }
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"

77
event_monitor/src/lib.rs Normal file
View File

@@ -0,0 +1,77 @@
// Copyright © 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[macro_use]
extern crate serde_derive;
use std::borrow::Cow;
use std::collections::HashMap;
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::time::{Duration, Instant};
static mut MONITOR: Option<(File, Instant)> = None;
/// This function must only be called once from the main process before any threads
/// are created to avoid race conditions
pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
assert!(unsafe { MONITOR.is_none() });
let fd = file.as_raw_fd();
let ret = unsafe {
let mut flags = libc::fcntl(fd, libc::F_GETFL);
flags |= libc::O_NONBLOCK;
libc::fcntl(fd, libc::F_SETFL, flags)
};
if ret < 0 {
return Err(std::io::Error::last_os_error());
}
unsafe {
MONITOR = Some((file, Instant::now()));
};
Ok(())
}
#[derive(Serialize)]
struct Event<'a> {
timestamp: Duration,
source: &'a str,
event: &'a str,
properties: Option<&'a HashMap<Cow<'a, str>, Cow<'a, str>>>,
}
pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
if let Some((file, start)) = unsafe { MONITOR.as_ref() } {
let e = Event {
timestamp: start.elapsed(),
source,
event,
properties,
};
serde_json::to_writer_pretty(file, &e).ok();
}
}
/*
Through the use of Cow<'a, str> it is possible to use String as well as
&str as the parameters:
e.g.
event!("cpu_manager", "create_vcpu", "id", cpu_id.to_string());
*/
#[macro_export]
macro_rules! event {
($source:expr, $event:expr) => {
$crate::event_log($source, $event, None)
};
($source:expr, $event:expr, $($key:expr, $value:expr),*) => {
{
let mut properties = ::std::collections::HashMap::new();
$(
properties.insert($key.into(), $value.into());
)+
$crate::event_log($source, $event, Some(&properties))
}
};
}

765
fuzz/Cargo.lock generated Normal file
View File

@@ -0,0 +1,765 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "acpi_tables"
version = "0.1.0"
dependencies = [
"vm-memory",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767"
[[package]]
name = "api_client"
version = "0.1.0"
[[package]]
name = "arbitrary"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "698b65a961a9d730fb45b6b0327e20207810c9f61ee421b082b27ba003f49e2b"
[[package]]
name = "arc-swap"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d7d63395147b81a9e570bcc6243aaf71c017bd666d4909cfef0085bdda8d73"
[[package]]
name = "arch"
version = "0.1.0"
dependencies = [
"acpi_tables",
"anyhow",
"arch_gen",
"byteorder",
"hypervisor",
"libc",
"linux-loader",
"log",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"vm-memory",
"vm-migration",
]
[[package]]
name = "arch_gen"
version = "0.1.0"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "block_util"
version = "0.1.0"
dependencies = [
"io-uring",
"libc",
"log",
"qcow",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"virtio-bindings",
"vm-memory",
"vm-virtio",
"vmm-sys-util",
]
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"term_size",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "cloud-hypervisor"
version = "0.13.0"
dependencies = [
"anyhow",
"api_client",
"clap",
"epoll",
"event_monitor",
"hypervisor",
"libc",
"log",
"option_parser",
"seccomp",
"serde_json",
"signal-hook",
"thiserror",
"vm-memory",
"vmm",
"vmm-sys-util",
]
[[package]]
name = "cloud-hypervisor-fuzz"
version = "0.0.0"
dependencies = [
"block_util",
"cloud-hypervisor",
"libc",
"libfuzzer-sys",
"qcow",
"seccomp",
"virtio-devices",
"vm-memory",
"vm-virtio",
"vmm-sys-util",
]
[[package]]
name = "devices"
version = "0.1.0"
dependencies = [
"acpi_tables",
"anyhow",
"bitflags",
"byteorder",
"epoll",
"libc",
"log",
"serde",
"serde_derive",
"serde_json",
"vm-device",
"vm-memory",
"vm-migration",
"vmm-sys-util",
]
[[package]]
name = "epoll"
version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0"
dependencies = [
"bitflags",
"libc",
]
[[package]]
name = "event_monitor"
version = "0.1.0"
dependencies = [
"libc",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "hermit-abi"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
[[package]]
name = "hypervisor"
version = "0.1.0"
dependencies = [
"anyhow",
"epoll",
"iced-x86",
"kvm-bindings",
"kvm-ioctls",
"libc",
"log",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"vm-memory",
"vmm-sys-util",
]
[[package]]
name = "iced-x86"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4261b15a556aa9b83b976b7aa4613f5a3b80351683af14c5d8d7e62c00869d5a"
dependencies = [
"lazy_static",
"static_assertions",
]
[[package]]
name = "io-uring"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb82832e05cc4ca298f198a8db108837b4f7b7b1248e3cba8e48f151aece80cf"
dependencies = [
"bitflags",
"libc",
]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "kvm-bindings"
version = "0.4.0"
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.4.0#1a68725639283e622f4bb64584885b30bfe8be44"
dependencies = [
"serde",
"serde_derive",
"vmm-sys-util",
]
[[package]]
name = "kvm-ioctls"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74058b16912c6723db02d4ca3ec919b73cd41512ccd3b6202cf91ae8d6c9dce5"
dependencies = [
"kvm-bindings",
"libc",
"vmm-sys-util",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
[[package]]
name = "libfuzzer-sys"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86c975d637bc2a2f99440932b731491fc34c7f785d239e38af3addd3c2fd0e46"
dependencies = [
"arbitrary",
"cc",
]
[[package]]
name = "linux-loader"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c819cc8275b0f2c1ed9feec455ca288b45d82932384a6a5f7a86812ee3427459"
dependencies = [
"vm-memory",
]
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "micro_http"
version = "0.1.0"
source = "git+https://github.com/firecracker-microvm/micro-http?branch=master#59ab64440a95f7b16253fef67b5201c2ec4adaf7"
dependencies = [
"epoll",
]
[[package]]
name = "net_gen"
version = "0.1.0"
dependencies = [
"vmm-sys-util",
]
[[package]]
name = "net_util"
version = "0.1.0"
dependencies = [
"epoll",
"libc",
"log",
"net_gen",
"serde",
"virtio-bindings",
"vm-memory",
"vm-virtio",
"vmm-sys-util",
]
[[package]]
name = "option_parser"
version = "0.1.0"
[[package]]
name = "pci"
version = "0.1.0"
dependencies = [
"anyhow",
"byteorder",
"hypervisor",
"libc",
"log",
"serde",
"serde_derive",
"serde_json",
"vfio-bindings",
"vfio-ioctls",
"vm-allocator",
"vm-device",
"vm-memory",
"vm-migration",
"vmm-sys-util",
]
[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid",
]
[[package]]
name = "qcow"
version = "0.1.0"
dependencies = [
"byteorder",
"libc",
"log",
"remain",
"vmm-sys-util",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "remain"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "seccomp"
version = "0.1.0"
source = "git+https://github.com/firecracker-microvm/firecracker?tag=v0.22.0#cc5387637c132e500b4857b80b5a239d8d732b77"
dependencies = [
"libc",
]
[[package]]
name = "serde"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
[[package]]
name = "serde_derive"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "signal-hook"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aa894ef3fade0ee7243422f4fbbd6c2b48e6de767e621d37ef65f2310f53cea"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
"libc",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "term_size"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"term_size",
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "vfio-bindings"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a21f546f2bda37f5a8cfb138c87f95b8e34d2d78d6a7a92ba3785f4e08604a7"
dependencies = [
"vmm-sys-util",
]
[[package]]
name = "vfio-ioctls"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=master#a87b13bdec026e8144b91f30f35451a966d8c1ca"
dependencies = [
"byteorder",
"kvm-bindings",
"kvm-ioctls",
"log",
"vfio-bindings",
"vm-memory",
"vmm-sys-util",
]
[[package]]
name = "vhost"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/vhost?branch=master#ee3e8722706c984b3dfe12d3a130e92101b78e8f"
dependencies = [
"bitflags",
"libc",
"vmm-sys-util",
]
[[package]]
name = "virtio-bindings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff512178285488516ed85f15b5d0113a7cdb89e9e8a760b269ae4f02b84bd6b"
[[package]]
name = "virtio-devices"
version = "0.1.0"
dependencies = [
"anyhow",
"arc-swap",
"block_util",
"byteorder",
"epoll",
"event_monitor",
"io-uring",
"libc",
"log",
"net_gen",
"net_util",
"pci",
"seccomp",
"serde",
"serde_derive",
"serde_json",
"vhost",
"virtio-bindings",
"vm-allocator",
"vm-device",
"vm-memory",
"vm-migration",
"vm-virtio",
"vmm-sys-util",
]
[[package]]
name = "vm-allocator"
version = "0.1.0"
dependencies = [
"arch",
"libc",
"vm-memory",
]
[[package]]
name = "vm-device"
version = "0.1.0"
dependencies = [
"anyhow",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"vfio-ioctls",
"vm-memory",
"vmm-sys-util",
]
[[package]]
name = "vm-memory"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625f401b1b8b3ac3d43f53903cd138cfe840bd985f8581e553027b31d2bb8ae8"
dependencies = [
"arc-swap",
"libc",
"winapi",
]
[[package]]
name = "vm-migration"
version = "0.1.0"
dependencies = [
"anyhow",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"vm-memory",
]
[[package]]
name = "vm-virtio"
version = "0.1.0"
dependencies = [
"log",
"serde",
"serde_derive",
"serde_json",
"virtio-bindings",
"vm-memory",
]
[[package]]
name = "vmm"
version = "0.1.0"
dependencies = [
"acpi_tables",
"anyhow",
"arc-swap",
"arch",
"bitflags",
"block_util",
"clap",
"devices",
"epoll",
"event_monitor",
"hypervisor",
"lazy_static",
"libc",
"linux-loader",
"log",
"micro_http",
"net_util",
"option_parser",
"pci",
"qcow",
"seccomp",
"serde",
"serde_derive",
"serde_json",
"signal-hook",
"thiserror",
"vfio-ioctls",
"virtio-devices",
"vm-allocator",
"vm-device",
"vm-memory",
"vm-migration",
"vm-virtio",
"vmm-sys-util",
]
[[package]]
name = "vmm-sys-util"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cf11afbc4ebc0d5c7a7748a77d19e2042677fc15faa2f4ccccb27c18a60605"
dependencies = [
"bitflags",
"libc",
"serde",
"serde_derive",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@@ -1,4 +1,3 @@
[package]
name = "cloud-hypervisor-fuzz"
version = "0.0.0"
@@ -10,26 +9,22 @@ edition = "2018"
cargo-fuzz = true
[dependencies]
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "master" }
libc = "0.2.72"
libfuzzer-sys = "0.3"
block_util = { path = "../block_util" }
libc = "0.2.90"
libfuzzer-sys = "0.4"
qcow = { path = "../qcow" }
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.22.0" }
virtio-devices = { path = "../virtio-devices" }
vmm-sys-util = ">=0.3.1"
vmm-sys-util = "0.8.0"
vm-virtio = { path = "../vm-virtio" }
vm-memory = "0.3.0"
vm-memory = "0.5.0"
[patch.crates-io]
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.4.0", features = ["with-serde", "fam-wrappers"] }
[dependencies.cloud-hypervisor]
path = ".."
[patch.crates-io]
vm-memory = { git = "https://github.com/cloud-hypervisor/vm-memory", branch = "ch" }
[patch.'https://github.com/rust-vmm/kvm-ioctls']
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
[patch.'https://github.com/rust-vmm/kvm-bindings']
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch", features = ["with-serde", "fam-wrappers"] }
# Prevent this from interfering with workspaces
[workspace]
members = ["."]

View File

@@ -4,7 +4,9 @@
#![no_main]
use block_util::{async_io::DiskFile, qcow_sync::QcowDiskSync};
use libfuzzer_sys::fuzz_target;
use seccomp::SeccompAction;
use std::ffi;
use std::fs::File;
use std::io::{self, Cursor, Read, Seek, SeekFrom};
@@ -16,7 +18,6 @@ use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
use vm_virtio::Queue;
use vmm_sys_util::eventfd::EventFd;
use seccomp::SeccompAction;
const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
@@ -83,17 +84,18 @@ fuzz_target!(|bytes| {
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
let disk_file: File = unsafe { File::from_raw_fd(shm) };
let raw_img = qcow::RawFile::new(disk_file, false);
let qcow_disk = Box::new(QcowDiskSync::new(disk_file, false)) as Box<dyn DiskFile>;
let mut block = Block::new(
"tmp".to_owned(),
raw_img,
qcow_disk,
PathBuf::from(""),
false,
false,
2,
256,
SeccompAction::Allow,
None,
)
.unwrap();

View File

@@ -6,22 +6,30 @@ edition = "2018"
license = "Apache-2.0 OR BSD-3-Clause"
[features]
kvm = []
kvm = ["kvm-ioctls", "kvm-bindings"]
mshv = ["mshv-ioctls", "mshv-bindings"]
tdx = []
[dependencies]
anyhow = "1.0"
arc-swap = ">=0.4.4"
epoll = ">=4.0.1"
thiserror = "1.0"
libc = "0.2.80"
log = "0.4.11"
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "master" }
kvm-bindings = { git = "https://github.com/rust-vmm/kvm-bindings", branch = "master", features = ["with-serde", "fam-wrappers"] }
libc = "0.2.91"
log = "0.4.14"
kvm-ioctls = { version = "0.8.0", optional = true }
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.4.0", features = ["with-serde", "fam-wrappers"], optional = true }
mshv-bindings = {git = "https://github.com/cloud-hypervisor/mshv", branch = "master", features = ["with-serde", "fam-wrappers"], optional = true }
mshv-ioctls = { git = "https://github.com/cloud-hypervisor/mshv", branch = "master", optional = true}
serde = {version = ">=1.0.27", features = ["rc"] }
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"
vm-memory = { version = "0.3.0", features = ["backend-mmap", "backend-atomic"] }
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
vmm-sys-util = { version = ">=0.5.0", features = ["with-serde"] }
[dependencies.linux-loader]
git = "https://github.com/rust-vmm/linux-loader"
features = ["elf", "bzimage"]
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
version = "1.11"
default-features = false
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
[dev-dependencies]
env_logger = "0.8.3"

View File

@@ -0,0 +1,148 @@
//
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
extern crate thiserror;
use core::fmt::Debug;
use std::fmt::{self, Display};
use thiserror::Error;
#[derive(Clone, Copy, Error, Debug)]
pub struct Exception<T: Debug> {
vector: T,
ip: u64,
error: Option<u32>,
payload: Option<u64>,
}
impl<T: Debug> Display for Exception<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Exception {:?} at IP {:#x}", self.vector, self.ip)
}
}
#[derive(Error, Debug)]
pub enum PlatformError {
#[error("Invalid address: {0}")]
InvalidAddress(#[source] anyhow::Error),
#[error("Invalid register: {0}")]
InvalidRegister(#[source] anyhow::Error),
#[error("Invalid state: {0}")]
InvalidState(#[source] anyhow::Error),
#[error("Memory read failure: {0}")]
MemoryReadFailure(#[source] anyhow::Error),
#[error("Memory write failure: {0}")]
MemoryWriteFailure(#[source] anyhow::Error),
#[error("Get CPU state failure: {0}")]
GetCpuStateFailure(#[source] anyhow::Error),
#[error("Set CPU state failure: {0}")]
SetCpuStateFailure(#[source] anyhow::Error),
#[error("Translate virtual address: {0}")]
TranslateVirtualAddress(#[source] anyhow::Error),
#[error("Unsupported CPU Mode: {0}")]
UnsupportedCpuMode(#[source] anyhow::Error),
#[error("Invalid instruction operand: {0}")]
InvalidOperand(#[source] anyhow::Error),
}
#[derive(Error, Debug)]
pub enum EmulationError<T: Debug> {
#[error("Unsupported instruction: {0}")]
UnsupportedInstruction(#[source] anyhow::Error),
#[error("Unsupported memory size: {0}")]
UnsupportedMemorySize(#[source] anyhow::Error),
#[error("Invalid operand: {0}")]
InvalidOperand(#[source] anyhow::Error),
#[error("Wrong number of operands: {0}")]
WrongNumberOperands(#[source] anyhow::Error),
#[error("Instruction Exception: {0}")]
InstructionException(Exception<T>),
#[error("Instruction fetching error: {0}")]
InstructionFetchingError(#[source] anyhow::Error),
#[error("Platform emulation error: {0}")]
PlatformEmulationError(PlatformError),
#[error(transparent)]
EmulationError(#[from] anyhow::Error),
}
/// The PlatformEmulator trait emulates a guest platform.
/// It's mostly a guest resources (memory and CPU state) getter and setter.
///
/// A CpuState is an architecture specific type, representing a CPU state.
/// The emulator and its instruction handlers modify a given CPU state and
/// eventually ask the platform to commit it back through `set_cpu_state`.
pub trait PlatformEmulator: Send + Sync {
type CpuState: Clone;
/// Read guest memory into a u8 slice.
///
/// # Arguments
///
/// * `gva` - Guest virtual address to read from.
/// * `data` - Data slice to read into.
///
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError>;
/// Write a u8 slice into guest memory.
///
/// # Arguments
///
/// * `gva` - Guest virtual address to write into.
/// * `data` - Data slice to be written.
///
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError>;
/// Get a CPU state from the guest.
///
/// # Arguments
///
/// * `cpu_id` - Logical CPU ID.
///
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError>;
/// Set a guest CPU state.
///
/// # Arguments
///
/// * `cpu_id` - Logical CPU ID.
/// * `state` - State to set the CPU into.
///
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError>;
/// Translate a guest virtual address into a physical one
///
/// # Arguments
///
/// * `gva` - Guest virtual address to translate.
///
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError>;
/// Fetch instruction bytes from memory.
///
/// # Arguments
///
/// * `ip` - Instruction pointer virtual address to start fetching instructions from.
///
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError>;
}
pub type EmulationResult<S, E> = std::result::Result<S, EmulationError<E>>;

View File

@@ -11,5 +11,8 @@
// Copyright © 2020, Microsoft Corporation
//
pub mod emulator;
#[cfg(target_arch = "x86_64")]
#[macro_use]
pub mod x86;

View File

@@ -0,0 +1,337 @@
//
// Copyright © 2020 Microsoft
//
// SPDX-License-Identifier: Apache-2.0
//
#![allow(non_camel_case_types, clippy::upper_case_acronyms)]
//
// CMP-Compare Two Operands
//
extern crate iced_x86;
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::regs::*;
use crate::arch::x86::Exception;
// CMP affects OF, SF, ZF, AF, PF and CF
const FLAGS_MASK: u64 = CF | PF | AF | ZF | SF | OF;
// TODO: Switch to inline asm when that's stable. Executing CMP (or any arthimetic instructions)
// natively and extracting RFLAGS will be much faster and make the code simpler.
fn calc_rflags_cpazso(op0: u64, op1: u64, op_size: usize) -> u64 {
let op_bits = op_size * 8;
let msb_shift = op_bits - 1;
// CMP is the same as SUB.
let result = op0.wrapping_sub(op1);
// Carry-out vector for SUB.
let cout = (!op0 & op1) | ((!op0 ^ op1) & result);
let cf = ((cout >> msb_shift) & 0x1) << CF_SHIFT;
// PF only needs the least significant byte. XOR its higher 4 bits with its lower 4 bits then
// use the value directly.
let pf = ((0x9669 >> ((result ^ (result >> 4)) & 0xf)) & 0x1) << PF_SHIFT;
// AF cares about the lowest 4 bits (nibble). msb_shift is 3 in this case.
let af = ((cout >> 3) & 0x1) << AF_SHIFT;
let zf = if result & (!0u64 >> (63 - msb_shift)) == 0 {
1
} else {
0
} << ZF_SHIFT;
let sf = ((result >> msb_shift) & 0x1) << SF_SHIFT;
// Overflow happens when two operands have the same sign but the result has a different sign.
let of = ((((op0 ^ op1) & (op0 ^ result)) >> msb_shift) & 0x1) << OF_SHIFT;
cf | pf | af | zf | sf | of
}
macro_rules! cmp_rm_r {
($bound:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform)
.map_err(EmulationError::PlatformEmulationError)?;
let op1_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
.map_err(EmulationError::PlatformEmulationError)?;
let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>());
state.set_flags((state.flags() & !FLAGS_MASK) | cpazso);
Ok(())
}
};
}
macro_rules! cmp_r_rm {
($bound:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform)
.map_err(EmulationError::PlatformEmulationError)?;
let op1_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
.map_err(EmulationError::PlatformEmulationError)?;
let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>());
state.set_flags((state.flags() & !FLAGS_MASK) | cpazso);
Ok(())
}
};
}
macro_rules! cmp_rm_imm {
($imm:ty, $bound:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform)
.map_err(EmulationError::PlatformEmulationError)?;
let op1_value = get_op(&insn, 1, std::mem::size_of::<$imm>(), state, platform)
.map_err(EmulationError::PlatformEmulationError)?;
let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>());
state.set_flags((state.flags() & !FLAGS_MASK) | cpazso);
Ok(())
}
};
}
pub struct Cmp_rm64_r64;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_r64 {
cmp_rm_r!(u64);
}
pub struct Cmp_rm32_r32;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_r32 {
cmp_rm_r!(u32);
}
pub struct Cmp_rm16_r16;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_r16 {
cmp_rm_r!(u16);
}
pub struct Cmp_rm8_r8;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm8_r8 {
cmp_rm_r!(u8);
}
pub struct Cmp_r64_rm64;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r64_rm64 {
cmp_r_rm!(u64);
}
pub struct Cmp_r32_rm32;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r32_rm32 {
cmp_r_rm!(u32);
}
pub struct Cmp_r16_rm16;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r16_rm16 {
cmp_r_rm!(u16);
}
pub struct Cmp_r8_rm8;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r8_rm8 {
cmp_r_rm!(u8);
}
pub struct Cmp_AL_imm8;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_AL_imm8 {
cmp_rm_imm!(u8, u8);
}
pub struct Cmp_AX_imm16;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_AX_imm16 {
cmp_rm_imm!(u16, u16);
}
pub struct Cmp_EAX_imm32;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_EAX_imm32 {
cmp_rm_imm!(u32, u32);
}
pub struct Cmp_RAX_imm32;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_RAX_imm32 {
cmp_rm_imm!(u32, u64);
}
pub struct Cmp_rm8_imm8;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm8_imm8 {
cmp_rm_imm!(u8, u8);
}
pub struct Cmp_rm16_imm16;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_imm16 {
cmp_rm_imm!(u16, u16);
}
pub struct Cmp_rm32_imm32;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_imm32 {
cmp_rm_imm!(u32, u32);
}
pub struct Cmp_rm64_imm32;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_imm32 {
cmp_rm_imm!(u32, u64);
}
pub struct Cmp_rm16_imm8;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_imm8 {
cmp_rm_imm!(u8, u16);
}
pub struct Cmp_rm32_imm8;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_imm8 {
cmp_rm_imm!(u8, u32);
}
pub struct Cmp_rm64_imm8;
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_imm8 {
cmp_rm_imm!(u8, u64);
}
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;
#[test]
// cmp ah,al
fn test_cmp_rm8_r8_1() {
let rax: u64 = 0x0;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x38, 0xc4]; // cmp ah,al
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
assert_eq!(0b1000100, rflags);
}
#[test]
// cmp eax,100
fn test_cmp_rm32_imm8_1() {
let rax: u64 = 0xabcdef;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x83, 0xf8, 0x64]; // cmp eax,100
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
assert_eq!(0b100, rflags);
}
#[test]
// cmp eax,-1
fn test_cmp_rm32_imm8_2() {
let rax: u64 = 0xabcdef;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x83, 0xf8, 0xff]; // cmp eax,-1
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
assert_eq!(0b101, rflags);
}
#[test]
// cmp rax,rbx
fn test_cmp_rm64_r64() {
let rax: u64 = 0xabcdef;
let rbx: u64 = 0x1234;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x48, 0x39, 0xd8, 0x00, 0xc3]; // cmp rax,rbx + two bytes garbage
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax), (Register::RBX, rbx)], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
assert_eq!(0b100, rflags);
}
#[test]
fn test_cmp_64() {
let data = [
(0xabcdef, 0x1234, 0b100),
(0x0, 0x101, 0b1001_0101),
(0x0, 0x8000_0000_0000_0000, 0b1000_1000_0101),
(0x1234abcd, 0x1234abcd, 0b100_0100),
(0x1234abcd, 0xdeadbeef, 0b1001_0101),
(0xffff_ffff_ffff_ffff, 0xdeadbeef, 0b1000_0000),
(0xffff_ffff_ffff_ffff, 0x0, 0b1000_0100),
];
for d in data.iter() {
let rax = d.0;
let rbx = d.1;
let insn = [0x48, 0x39, 0xd8]; // cmp rax,rbx
let mut vmm = MockVmm::new(
0x1000,
vec![(Register::RAX, rax), (Register::RBX, rbx)],
None,
);
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK;
assert_eq!(d.2, rflags);
}
}
#[test]
fn test_cmp_32() {
let data = [
(0xabcdef, 0x1234, 0b100),
(0x0, 0x101, 0b1001_0101),
(0x0, 0x8000_0000_0000_0000, 0b100_0100), // Same as cmp 0,0 due to truncation
(0x1234abcd, 0x1234abcd, 0b100_0100),
(0x1234abcd, 0xdeadbeef, 0b1_0101),
(0xffff_ffff_ffff_ffff, 0xdeadbeef, 0b0), // Same as cmp 0xffffffff,0xdeadbeef
(0xffff_ffff, 0x0, 0b1000_0100),
];
for d in data.iter() {
let rax = d.0;
let rbx = d.1;
let insn = [0x39, 0xd8]; // cmp eax,ebx
let mut vmm = MockVmm::new(
0x1000,
vec![(Register::RAX, rax), (Register::RBX, rbx)],
None,
);
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK;
assert_eq!(d.2, rflags);
}
}
}

View File

@@ -0,0 +1,160 @@
//
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
extern crate iced_x86;
use crate::arch::emulator::{EmulationError, PlatformEmulator, PlatformError};
use crate::arch::x86::emulator::CpuStateManager;
use crate::arch::x86::Exception;
use iced_x86::*;
pub mod cmp;
pub mod mov;
pub mod movs;
fn get_op<T: CpuStateManager>(
insn: &Instruction,
op_index: u32,
op_size: usize,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<u64, PlatformError> {
if insn.op_count() < op_index + 1 {
return Err(PlatformError::InvalidOperand(anyhow!(
"Invalid operand {:?}",
op_index
)));
}
match op_size {
1 | 2 | 4 | 8 => {}
_ => {
return Err(PlatformError::InvalidOperand(anyhow!(
"Invalid operand size {:?}",
op_size
)))
}
}
let value = match insn
.try_op_kind(op_index)
.map_err(|e| PlatformError::InvalidOperand(e.into()))?
{
OpKind::Register => state.read_reg(
insn.try_op_register(op_index)
.map_err(|e| PlatformError::InvalidOperand(e.into()))?,
)?,
OpKind::Memory => {
let addr = memory_operand_address(insn, state, false)?;
let mut memory: [u8; 8] = [0; 8];
platform.read_memory(addr, &mut memory[0..op_size])?;
<u64>::from_le_bytes(memory)
}
OpKind::Immediate8 => insn.immediate8() as u64,
OpKind::Immediate8to16 => insn.immediate8to16() as u64,
OpKind::Immediate8to32 => insn.immediate8to32() as u64,
OpKind::Immediate8to64 => insn.immediate8to64() as u64,
OpKind::Immediate16 => insn.immediate16() as u64,
OpKind::Immediate32 => insn.immediate32() as u64,
OpKind::Immediate32to64 => insn.immediate32to64() as u64,
OpKind::Immediate64 => insn.immediate64() as u64,
k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))),
};
Ok(value)
}
fn set_op<T: CpuStateManager>(
insn: &Instruction,
op_index: u32,
op_size: usize,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
value: u64,
) -> Result<(), PlatformError> {
if insn.op_count() < op_index + 1 {
return Err(PlatformError::InvalidOperand(anyhow!(
"Invalid operand {:?}",
op_index
)));
}
match op_size {
1 | 2 | 4 | 8 => {}
_ => {
return Err(PlatformError::InvalidOperand(anyhow!(
"Invalid operand size {:?}",
op_size
)))
}
}
match insn
.try_op_kind(op_index)
.map_err(|e| PlatformError::InvalidOperand(e.into()))?
{
OpKind::Register => state.write_reg(
insn.try_op_register(op_index)
.map_err(|e| PlatformError::InvalidOperand(e.into()))?,
value,
)?,
OpKind::Memory => {
let addr = memory_operand_address(insn, state, true)?;
platform.write_memory(addr, &value.to_le_bytes()[..op_size])?;
}
k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))),
};
Ok(())
}
// Returns the linear a.k.a. virtual address for a memory operand.
fn memory_operand_address<T: CpuStateManager>(
insn: &Instruction,
state: &T,
write: bool,
) -> Result<u64, PlatformError> {
let mut address: u64 = 0;
if insn.memory_base() != iced_x86::Register::None {
let base: u64 = state.read_reg(insn.memory_base())?;
address += base;
}
if insn.memory_index() != iced_x86::Register::None {
let mut index: u64 = state.read_reg(insn.memory_index())?;
index *= insn.memory_index_scale() as u64;
address += index;
}
address += insn.memory_displacement64();
// Translate to a linear address.
state.linearize(insn.memory_segment(), address, write)
}
pub trait InstructionHandler<T: CpuStateManager> {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>>;
}
macro_rules! insn_format {
($insn:ident) => {{
let mut output = String::new();
let mut formatter = FastFormatter::new();
formatter
.options_mut()
.set_space_after_operand_separator(true);
formatter.format(&$insn, &mut output);
output
}};
}

View File

@@ -0,0 +1,622 @@
//
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#![allow(non_camel_case_types)]
//
// MOV-Move
// SDM Volume 1, Chapter 4.3
// Copies the second operand (source operand) to the first operand (destination operand).
//
extern crate iced_x86;
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::Exception;
macro_rules! mov_rm_r {
($bound:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
let src_reg_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
.map_err(EmulationError::PlatformEmulationError)?;
set_op(
&insn,
0,
std::mem::size_of::<$bound>(),
state,
platform,
src_reg_value,
)
.map_err(EmulationError::PlatformEmulationError)?;
Ok(())
}
};
}
macro_rules! mov_rm_imm {
($bound:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
let imm = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
.map_err(EmulationError::PlatformEmulationError)?;
set_op(
&insn,
0,
std::mem::size_of::<$bound>(),
state,
platform,
imm,
)
.map_err(EmulationError::PlatformEmulationError)?;
Ok(())
}
};
}
macro_rules! movzx {
($dest_op_size:ty, $src_op_size:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
let src_value = get_op(
&insn,
1,
std::mem::size_of::<$src_op_size>(),
state,
platform,
)
.map_err(EmulationError::PlatformEmulationError)?;
set_op(
&insn,
0,
std::mem::size_of::<$dest_op_size>(),
state,
platform,
src_value,
)
.map_err(EmulationError::PlatformEmulationError)?;
Ok(())
}
};
}
// MOV r/rm is a special case of MOVZX, where both operands have the same size.
macro_rules! mov_r_rm {
($op_size:ty) => {
movzx!($op_size, $op_size);
};
}
macro_rules! mov_r_imm {
($bound:ty) => {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
let imm = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
.map_err(EmulationError::PlatformEmulationError)?;
set_op(
&insn,
0,
std::mem::size_of::<$bound>(),
state,
platform,
imm,
)
.map_err(EmulationError::PlatformEmulationError)?;
Ok(())
}
};
}
pub struct Mov_r8_rm8;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_rm8 {
mov_r_rm!(u8);
}
pub struct Mov_r8_imm8;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_imm8 {
mov_r_imm!(u8);
}
pub struct Mov_r16_rm16;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_rm16 {
mov_r_rm!(u16);
}
pub struct Mov_r16_imm16;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_imm16 {
mov_r_imm!(u16);
}
pub struct Mov_r32_rm32;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_rm32 {
mov_r_rm!(u32);
}
pub struct Mov_r32_imm32;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_imm32 {
mov_r_imm!(u32);
}
pub struct Mov_r64_rm64;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_rm64 {
mov_r_rm!(u64);
}
pub struct Mov_r64_imm64;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_imm64 {
mov_r_imm!(u64);
}
pub struct Mov_rm8_imm8;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_imm8 {
mov_rm_imm!(u8);
}
pub struct Mov_rm8_r8;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_r8 {
mov_rm_r!(u8);
}
pub struct Mov_rm16_imm16;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_imm16 {
mov_rm_imm!(u16);
}
pub struct Mov_rm16_r16;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_r16 {
mov_rm_r!(u16);
}
pub struct Mov_rm32_imm32;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_imm32 {
mov_rm_imm!(u32);
}
pub struct Mov_rm32_r32;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_r32 {
mov_rm_r!(u32);
}
pub struct Mov_rm64_imm32;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_imm32 {
mov_rm_imm!(u32);
}
pub struct Mov_rm64_r64;
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_r64 {
mov_rm_r!(u64);
}
// MOVZX
pub struct Movzx_r16_rm8;
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r16_rm8 {
movzx!(u16, u8);
}
pub struct Movzx_r32_rm8;
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r32_rm8 {
movzx!(u32, u8);
}
pub struct Movzx_r64_rm8;
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r64_rm8 {
movzx!(u64, u8);
}
pub struct Movzx_r32_rm16;
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r32_rm16 {
movzx!(u32, u16);
}
pub struct Movzx_r64_rm16;
impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r64_rm16 {
movzx!(u64, u16);
}
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;
#[test]
// mov rax,rbx
fn test_mov_r64_r64() {
let rbx: u64 = 0x8899aabbccddeeff;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x48, 0x89, 0xd8];
let mut vmm = MockVmm::new(ip, vec![(Register::RBX, rbx)], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let rax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, rbx);
}
#[test]
// mov rax,0x1122334411223344
fn test_mov_r64_imm64() {
let imm64: u64 = 0x1122334411223344;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x48, 0xb8, 0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11];
let mut vmm = MockVmm::new(ip, vec![], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let rax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, imm64);
}
#[test]
// mov rax, [rax+rax]
fn test_mov_r64_m64() {
let target_rax: u64 = 0x1234567812345678;
let mut rax: u64 = 0x100;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory: [u8; 8] = target_rax.to_le_bytes();
let insn = [0x48, 0x8b, 0x04, 0x00];
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], Some((rax + rax, &memory)));
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
rax = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, target_rax);
}
#[test]
// mov al,0x11
fn test_mov_r8_imm8() {
let imm8: u8 = 0x11;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0xb0, 0x11];
let mut vmm = MockVmm::new(ip, vec![], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let al = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::AL)
.unwrap();
assert_eq!(al as u8, imm8);
}
#[test]
// mov eax,0x11
fn test_mov_r32_imm8() {
let imm8: u8 = 0x11;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0xb8, 0x11, 0x00, 0x00, 0x00];
let mut vmm = MockVmm::new(ip, vec![], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let eax = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::EAX)
.unwrap();
assert_eq!(eax as u8, imm8);
}
#[test]
// mov rax,0x11223344
fn test_mov_r64_imm32() {
let imm32: u32 = 0x11223344;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x48, 0xc7, 0xc0, 0x44, 0x33, 0x22, 0x11];
let mut vmm = MockVmm::new(ip, vec![], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let rax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, imm32 as u64);
}
#[test]
// mov byte ptr [rax],dh
fn test_mov_m8_r8() {
let rax: u64 = 0x100;
let dh: u8 = 0x99;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x88, 0x30];
let mut vmm = MockVmm::new(
ip,
vec![(Register::RAX, rax), (Register::DH, dh.into())],
None,
);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let mut memory: [u8; 1] = [0; 1];
vmm.read_memory(rax, &mut memory).unwrap();
assert_eq!(u8::from_le_bytes(memory), dh);
}
#[test]
// mov dword ptr [rax],esi
fn test_mov_m32_r32() {
let rax: u64 = 0x100;
let esi: u32 = 0x8899;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x89, 0x30];
let mut vmm = MockVmm::new(
ip,
vec![(Register::RAX, rax), (Register::ESI, esi.into())],
None,
);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let mut memory: [u8; 4] = [0; 4];
vmm.read_memory(rax, &mut memory).unwrap();
assert_eq!(u32::from_le_bytes(memory), esi);
}
#[test]
// mov dword ptr [rax+0x00000001],edi
fn test_mov_m32imm32_r32() {
let rax: u64 = 0x100;
let displacement: u64 = 0x1;
let edi: u32 = 0x8899;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x89, 0x3c, 0x05, 0x01, 0x00, 0x00, 0x00];
let mut vmm = MockVmm::new(
ip,
vec![(Register::RAX, rax), (Register::EDI, edi.into())],
None,
);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let mut memory: [u8; 4] = [0; 4];
vmm.read_memory(rax + displacement, &mut memory).unwrap();
assert_eq!(u32::from_le_bytes(memory), edi);
}
#[test]
// mov eax,dword ptr [rax+10h]
fn test_mov_r32_m32imm32() {
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let eax: u32 = 0xaabbccdd;
let memory: [u8; 4] = eax.to_le_bytes();
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x8b, 0x40, 0x10];
let mut vmm = MockVmm::new(
ip,
vec![(Register::RAX, rax)],
Some((rax + displacement, &memory)),
);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let new_eax = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::EAX)
.unwrap();
assert_eq!(new_eax, eax as u64);
}
#[test]
// mov al,byte ptr [rax+10h]
fn test_mov_r8_m32imm32() {
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let al: u8 = 0xaa;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x8a, 0x40, 0x10];
let memory: [u8; 1] = al.to_le_bytes();
let mut vmm = MockVmm::new(
ip,
vec![(Register::RAX, rax)],
Some((rax + displacement, &memory)),
);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let new_al = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::AL)
.unwrap();
assert_eq!(new_al, al as u64);
}
#[test]
// mov rax, 0x100
// mov rbx, qword ptr [rax+10h]
fn test_mov_r64_imm64_and_r64_m64() {
let target_rax: u64 = 0x1234567812345678;
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory: [u8; 8] = target_rax.to_le_bytes();
let insn = [
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
];
let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
let rbx: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RBX)
.unwrap();
assert_eq!(rbx, target_rax);
}
#[test]
// mov rax, 0x100
// mov rbx, qword ptr [rax+10h]
fn test_mov_r64_imm64_and_r64_m64_first_insn() {
let target_rax: u64 = 0x1234567812345678;
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory: [u8; 8] = target_rax.to_le_bytes();
let insn = [
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
];
let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
// Only run the first instruction.
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
assert_eq!(ip + 7, vmm.cpu_state(cpu_id).unwrap().ip());
let new_rax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, new_rax);
}
#[test]
// mov rax, 0x100
// mov rbx, qword ptr [rax+10h]
// mov rax, 0x200
fn test_mov_r64_imm64_and_r64_m64_two_insns() {
let target_rax: u64 = 0x1234567812345678;
let rax: u64 = 0x100;
let displacement: u64 = 0x10;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory: [u8; 8] = target_rax.to_le_bytes();
let insn = [
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
0x48, 0xc7, 0xc0, 0x00, 0x02, 0x00, 0x00, // mov rax, 0x200
];
let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
// Run the 2 first instructions.
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
assert_eq!(ip + 7 + 4, vmm.cpu_state(cpu_id).unwrap().ip());
let rbx: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RBX)
.unwrap();
assert_eq!(rbx, target_rax);
// Check that rax is still at 0x100
let new_rax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, new_rax);
}
#[test]
// movzx eax, bl
fn test_movzx_r32_r8l() {
let bx: u16 = 0x8899;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x0f, 0xb6, 0xc3];
let mut vmm = MockVmm::new(ip, vec![(Register::BX, bx as u64)], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let eax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::EAX)
.unwrap();
assert_eq!(eax, (bx & 0xff) as u64);
}
#[test]
// movzx eax, bh
fn test_movzx_r32_r8h() {
let bx: u16 = 0x8899;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x0f, 0xb6, 0xc7];
let mut vmm = MockVmm::new(ip, vec![(Register::BX, bx as u64)], None);
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let eax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::EAX)
.unwrap();
assert_eq!(eax, (bx >> 8) as u64);
}
#[test]
// movzx eax, byte ptr [rbx]
fn test_movzx_r32_m8() {
let rbx: u64 = 0x100;
let value: u8 = 0xaa;
let ip: u64 = 0x1000;
let cpu_id = 0;
let insn = [0x0f, 0xb7, 0x03];
let memory: [u8; 1] = value.to_le_bytes();
let mut vmm = MockVmm::new(ip, vec![(Register::RBX, rbx)], Some((rbx, &memory)));
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
let eax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::EAX)
.unwrap();
assert_eq!(eax, value as u64);
}
}

View File

@@ -0,0 +1,147 @@
//
// Copyright © 2021 Microsoft
//
// SPDX-License-Identifier: Apache-2.0
//
#![allow(non_camel_case_types)]
//
// MOVS - Move Data from String to String
//
extern crate iced_x86;
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::regs::DF;
use crate::arch::x86::Exception;
pub struct Movsd_m32_m32;
impl<T: CpuStateManager> InstructionHandler<T> for Movsd_m32_m32 {
fn emulate(
&self,
insn: &Instruction,
state: &mut T,
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
let mut count: u64 = if insn.has_rep_prefix() {
state
.read_reg(Register::ECX)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?
} else {
1
};
let mut rsi = state
.read_reg(Register::RSI)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
let mut rdi = state
.read_reg(Register::RDI)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
let df = (state.flags() & DF) != 0;
let len = std::mem::size_of::<u32>();
while count > 0 {
let mut memory: [u8; 4] = [0; 4];
let src = state
.linearize(Register::DS, rsi, false)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
let dst = state
.linearize(Register::ES, rdi, true)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
platform
.read_memory(src, &mut memory[0..len])
.map_err(EmulationError::PlatformEmulationError)?;
platform
.write_memory(dst, &memory[0..len])
.map_err(EmulationError::PlatformEmulationError)?;
if df {
rsi = rsi.wrapping_sub(len as u64);
rdi = rdi.wrapping_sub(len as u64);
} else {
rsi = rsi.wrapping_add(len as u64);
rdi = rdi.wrapping_add(len as u64);
}
count -= 1;
}
state
.write_reg(Register::RSI, rsi)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
state
.write_reg(Register::RDI, rdi)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
if insn.has_rep_prefix() {
state
.write_reg(Register::ECX, 0)
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;
#[test]
fn test_rep_movsd_m32_m32() {
let ip: u64 = 0x1000;
let memory: [u8; 24] = [
0x78, 0x56, 0x34, 0x12, // 0x12345678
0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
0x00, 0x00, 0x00, 0x00, // 0x00000000
0x00, 0x00, 0x00, 0x00, // 0x00000000
0x00, 0x00, 0x00, 0x00, // 0x00000000
];
let insn = [0xf3, 0xa5]; // rep movsd
let regs = vec![(Register::ECX, 3), (Register::ESI, 0), (Register::EDI, 0xc)];
let mut data = [0u8; 4];
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
vmm.read_memory(0xc, &mut data).unwrap();
assert_eq!(0x12345678, <u32>::from_le_bytes(data));
vmm.read_memory(0xc + 4, &mut data).unwrap();
assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
vmm.read_memory(0xc + 8, &mut data).unwrap();
assert_eq!(0x5aa55aa5, <u32>::from_le_bytes(data));
// The rest should be default value 0 from MockVmm
vmm.read_memory(0xc + 12, &mut data).unwrap();
assert_eq!(0x0, <u32>::from_le_bytes(data));
}
#[test]
fn test_movsd_m32_m32() {
let ip: u64 = 0x1000;
let memory: [u8; 4] = [
0x78, 0x56, 0x34, 0x12, // 0x12345678
];
let insn = [0xa5]; // movsd
let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
let mut data = [0u8; 4];
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
vmm.read_memory(0x8, &mut data).unwrap();
assert_eq!(0x12345678, <u32>::from_le_bytes(data));
// The rest should be default value 0 from MockVmm
vmm.read_memory(0x4, &mut data).unwrap();
assert_eq!(0x0, <u32>::from_le_bytes(data));
vmm.read_memory(0x8 + 8, &mut data).unwrap();
assert_eq!(0x0, <u32>::from_le_bytes(data));
}
}

View File

@@ -0,0 +1,863 @@
//
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
extern crate iced_x86;
use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError};
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::regs::*;
use crate::arch::x86::*;
use crate::arch::x86::{Exception, SegmentRegisterOps};
use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters};
use anyhow::Context;
use iced_x86::*;
#[macro_use]
mod instructions;
/// x86 CPU modes
#[derive(Debug, PartialEq)]
pub enum CpuMode {
/// Real mode
Real,
/// Virtual 8086 mode
Virtual8086,
/// 16-bit protected mode
Protected16,
/// 32-bit protected mode
Protected,
/// 64-bit mode, a.k.a. long mode
Long,
}
/// CpuStateManager manages an x86 CPU state.
///
/// Instruction emulation handlers get a mutable reference to
/// a `CpuStateManager` implementation, representing the current state of the
/// CPU they have to emulate an instruction stream against. Usually those
/// handlers will modify the CPU state by modifying `CpuState` and it is up to
/// the handler caller to commit those changes back by invoking a
/// `PlatformEmulator` implementation `set_state()` method.
///
pub trait CpuStateManager: Clone {
/// Reads a CPU register.
///
/// # Arguments
///
/// * `reg` - A general purpose, control or debug register.
fn read_reg(&self, reg: Register) -> Result<u64, PlatformError>;
/// Write to a CPU register.
///
/// # Arguments
///
/// * `reg` - A general purpose, control or debug register.
/// * `val` - The value to load.
fn write_reg(&mut self, reg: Register, val: u64) -> Result<(), PlatformError>;
/// Reads a segment register.
///
/// # Arguments
///
/// * `reg` - A segment register.
fn read_segment(&self, reg: Register) -> Result<SegmentRegister, PlatformError>;
/// Write to a segment register.
///
/// # Arguments
///
/// * `reg` - A segment register.
/// * `segment_reg` - The segment register value to load.
fn write_segment(
&mut self,
reg: Register,
segment_reg: SegmentRegister,
) -> Result<(), PlatformError>;
/// Get the CPU instruction pointer.
fn ip(&self) -> u64;
/// Set the CPU instruction pointer.
///
/// # Arguments
///
/// * `ip` - The CPU instruction pointer.
fn set_ip(&mut self, ip: u64);
/// Get the CPU Extended Feature Enable Register.
fn efer(&self) -> u64;
/// Set the CPU Extended Feature Enable Register.
///
/// # Arguments
///
/// * `efer` - The CPU EFER value.
fn set_efer(&mut self, efer: u64);
/// Get the CPU flags.
fn flags(&self) -> u64;
/// Set the CPU flags.
///
/// # Arguments
///
/// * `flags` - The CPU flags
fn set_flags(&mut self, flags: u64);
/// Get the CPU mode.
fn mode(&self) -> Result<CpuMode, PlatformError>;
/// Translate a logical (segmented) address into a linear (virtual) one.
///
/// # Arguments
///
/// * `segment` - Which segment to use for linearization
/// * `logical_addr` - The logical address to be translated
fn linearize(
&self,
segment: Register,
logical_addr: u64,
write: bool,
) -> Result<u64, PlatformError> {
let segment_register = self.read_segment(segment)?;
let mode = self.mode()?;
match mode {
CpuMode::Long => {
// TODO Check that we got a canonical address.
Ok(logical_addr
.checked_add(segment_register.base)
.ok_or_else(|| {
PlatformError::InvalidAddress(anyhow!(
"Logical address {:#x} can not be linearized with segment {:#x?}",
logical_addr,
segment_register
))
})?)
}
CpuMode::Protected | CpuMode::Real => {
let segment_type = segment_register.segment_type();
// Must not write to a read-only segment.
if segment_type_ro(segment_type) && write {
return Err(PlatformError::InvalidAddress(anyhow!(
"Can not write to a read-only segment"
)));
}
let logical_addr = logical_addr & 0xffff_ffffu64;
let mut segment_limit: u32 = if segment_register.granularity() != 0 {
(segment_register.limit << 12) | 0xfff
} else {
segment_register.limit
};
// Expand-down segment
if segment_type_expand_down(segment_type) {
if logical_addr >= segment_limit.into() {
return Err(PlatformError::InvalidAddress(anyhow!(
"{:#x} is off limits {:#x} (expand down)",
logical_addr,
segment_limit
)));
}
if segment_register.db() != 0 {
segment_limit = 0xffffffff
} else {
segment_limit = 0xffff
}
}
if logical_addr > segment_limit.into() {
return Err(PlatformError::InvalidAddress(anyhow!(
"{:#x} is off limits {:#x}",
logical_addr,
segment_limit
)));
}
Ok(logical_addr + segment_register.base)
}
_ => Err(PlatformError::UnsupportedCpuMode(anyhow!("{:?}", mode))),
}
}
}
const REGISTER_MASK_64: u64 = 0xffff_ffff_ffff_ffffu64;
const REGISTER_MASK_32: u64 = 0xffff_ffffu64;
const REGISTER_MASK_16: u64 = 0xffffu64;
const REGISTER_MASK_8: u64 = 0xffu64;
macro_rules! set_reg {
($reg:expr, $mask:expr, $value:expr) => {
$reg = ($reg & $mask) | $value
};
}
#[derive(Clone, Default, Debug)]
/// A minimal, emulated CPU state.
///
/// Hypervisors needing x86 emulation can choose to either use their own
/// CPU state structures and implement the CpuStateManager interface for it,
/// or use `EmulatorCpuState`. The latter implies creating a new state
/// `EmulatorCpuState` instance for each platform `cpu_state()` call, which
/// might be less efficient.
pub struct EmulatorCpuState {
pub regs: StandardRegisters,
pub sregs: SpecialRegisters,
}
impl CpuStateManager for EmulatorCpuState {
fn read_reg(&self, reg: Register) -> Result<u64, PlatformError> {
let mut reg_value: u64 = match reg {
Register::RAX | Register::EAX | Register::AX | Register::AL | Register::AH => {
self.regs.rax
}
Register::RBX | Register::EBX | Register::BX | Register::BL | Register::BH => {
self.regs.rbx
}
Register::RCX | Register::ECX | Register::CX | Register::CL | Register::CH => {
self.regs.rcx
}
Register::RDX | Register::EDX | Register::DX | Register::DL | Register::DH => {
self.regs.rdx
}
Register::RSP | Register::ESP | Register::SP => self.regs.rsp,
Register::RBP | Register::EBP | Register::BP => self.regs.rbp,
Register::RSI | Register::ESI | Register::SI | Register::SIL => self.regs.rsi,
Register::RDI | Register::EDI | Register::DI | Register::DIL => self.regs.rdi,
Register::R8 | Register::R8D | Register::R8W | Register::R8L => self.regs.r8,
Register::R9 | Register::R9D | Register::R9W | Register::R9L => self.regs.r9,
Register::R10 | Register::R10D | Register::R10W | Register::R10L => self.regs.r10,
Register::R11 | Register::R11D | Register::R11W | Register::R11L => self.regs.r11,
Register::R12 | Register::R12D | Register::R12W | Register::R12L => self.regs.r12,
Register::R13 | Register::R13D | Register::R13W | Register::R13L => self.regs.r13,
Register::R14 | Register::R14D | Register::R14W | Register::R14L => self.regs.r14,
Register::R15 | Register::R15D | Register::R15W | Register::R15L => self.regs.r15,
Register::CR0 => self.sregs.cr0,
Register::CR2 => self.sregs.cr2,
Register::CR3 => self.sregs.cr3,
Register::CR4 => self.sregs.cr4,
Register::CR8 => self.sregs.cr8,
r => {
return Err(PlatformError::InvalidRegister(anyhow!(
"read_reg invalid GPR {:?}",
r
)))
}
};
reg_value = if reg.is_gpr64() || reg.is_cr() {
reg_value
} else if reg.is_gpr32() {
reg_value & REGISTER_MASK_32
} else if reg.is_gpr16() {
reg_value & REGISTER_MASK_16
} else if reg.is_gpr8() {
if reg == Register::AH
|| reg == Register::BH
|| reg == Register::CH
|| reg == Register::DH
{
(reg_value >> 8) & REGISTER_MASK_8
} else {
reg_value & REGISTER_MASK_8
}
} else {
return Err(PlatformError::InvalidRegister(anyhow!(
"read_reg invalid GPR {:?}",
reg
)));
};
debug!("Register read: {:#x} from {:?}", reg_value, reg);
Ok(reg_value)
}
fn write_reg(&mut self, reg: Register, val: u64) -> Result<(), PlatformError> {
debug!("Register write: {:#x} to {:?}", val, reg);
// SDM Vol 1 - 3.4.1.1
//
// 8-bit and 16-bit operands generate an 8-bit or 16-bit result.
// The upper 56 bits or 48 bits (respectively) of the destination
// general-purpose register are not modified by the operation.
let (reg_value, mask): (u64, u64) = if reg.is_gpr64() || reg.is_cr() {
(val, !REGISTER_MASK_64)
} else if reg.is_gpr32() {
(val & REGISTER_MASK_32, !REGISTER_MASK_64)
} else if reg.is_gpr16() {
(val & REGISTER_MASK_16, !REGISTER_MASK_16)
} else if reg.is_gpr8() {
if reg == Register::AH
|| reg == Register::BH
|| reg == Register::CH
|| reg == Register::DH
{
((val & REGISTER_MASK_8) << 8, !(REGISTER_MASK_8 << 8))
} else {
(val & REGISTER_MASK_8, !REGISTER_MASK_8)
}
} else {
return Err(PlatformError::InvalidRegister(anyhow!(
"write_reg invalid register {:?}",
reg
)));
};
match reg {
Register::RAX | Register::EAX | Register::AX | Register::AL | Register::AH => {
set_reg!(self.regs.rax, mask, reg_value);
}
Register::RBX | Register::EBX | Register::BX | Register::BL | Register::BH => {
set_reg!(self.regs.rbx, mask, reg_value);
}
Register::RCX | Register::ECX | Register::CX | Register::CL | Register::CH => {
set_reg!(self.regs.rcx, mask, reg_value);
}
Register::RDX | Register::EDX | Register::DX | Register::DL | Register::DH => {
set_reg!(self.regs.rdx, mask, reg_value);
}
Register::RSP | Register::ESP | Register::SP => {
set_reg!(self.regs.rsp, mask, reg_value)
}
Register::RBP | Register::EBP | Register::BP => {
set_reg!(self.regs.rbp, mask, reg_value)
}
Register::RSI | Register::ESI | Register::SI | Register::SIL => {
set_reg!(self.regs.rsi, mask, reg_value)
}
Register::RDI | Register::EDI | Register::DI | Register::DIL => {
set_reg!(self.regs.rdi, mask, reg_value)
}
Register::R8 | Register::R8D | Register::R8W | Register::R8L => {
set_reg!(self.regs.r8, mask, reg_value)
}
Register::R9 | Register::R9D | Register::R9W | Register::R9L => {
set_reg!(self.regs.r9, mask, reg_value)
}
Register::R10 | Register::R10D | Register::R10W | Register::R10L => {
set_reg!(self.regs.r10, mask, reg_value)
}
Register::R11 | Register::R11D | Register::R11W | Register::R11L => {
set_reg!(self.regs.r11, mask, reg_value)
}
Register::R12 | Register::R12D | Register::R12W | Register::R12L => {
set_reg!(self.regs.r12, mask, reg_value)
}
Register::R13 | Register::R13D | Register::R13W | Register::R13L => {
set_reg!(self.regs.r13, mask, reg_value)
}
Register::R14 | Register::R14D | Register::R14W | Register::R14L => {
set_reg!(self.regs.r14, mask, reg_value)
}
Register::R15 | Register::R15D | Register::R15W | Register::R15L => {
set_reg!(self.regs.r15, mask, reg_value)
}
Register::CR0 => set_reg!(self.sregs.cr0, mask, reg_value),
Register::CR2 => set_reg!(self.sregs.cr2, mask, reg_value),
Register::CR3 => set_reg!(self.sregs.cr3, mask, reg_value),
Register::CR4 => set_reg!(self.sregs.cr4, mask, reg_value),
Register::CR8 => set_reg!(self.sregs.cr8, mask, reg_value),
_ => {
return Err(PlatformError::InvalidRegister(anyhow!(
"write_reg invalid register {:?}",
reg
)))
}
}
Ok(())
}
fn read_segment(&self, reg: Register) -> Result<SegmentRegister, PlatformError> {
if !reg.is_segment_register() {
return Err(PlatformError::InvalidRegister(anyhow!(
"read_segment {:?} is not a segment register",
reg
)));
}
match reg {
Register::CS => Ok(self.sregs.cs),
Register::DS => Ok(self.sregs.ds),
Register::ES => Ok(self.sregs.es),
Register::FS => Ok(self.sregs.fs),
Register::GS => Ok(self.sregs.gs),
Register::SS => Ok(self.sregs.ss),
r => Err(PlatformError::InvalidRegister(anyhow!(
"read_segment invalid register {:?}",
r
))),
}
}
fn write_segment(
&mut self,
reg: Register,
segment_register: SegmentRegister,
) -> Result<(), PlatformError> {
if !reg.is_segment_register() {
return Err(PlatformError::InvalidRegister(anyhow!("{:?}", reg)));
}
match reg {
Register::CS => self.sregs.cs = segment_register,
Register::DS => self.sregs.ds = segment_register,
Register::ES => self.sregs.es = segment_register,
Register::FS => self.sregs.fs = segment_register,
Register::GS => self.sregs.gs = segment_register,
Register::SS => self.sregs.ss = segment_register,
r => return Err(PlatformError::InvalidRegister(anyhow!("{:?}", r))),
}
Ok(())
}
fn ip(&self) -> u64 {
self.regs.rip
}
fn set_ip(&mut self, ip: u64) {
self.regs.rip = ip;
}
fn efer(&self) -> u64 {
self.sregs.efer
}
fn set_efer(&mut self, efer: u64) {
self.sregs.efer = efer
}
fn flags(&self) -> u64 {
self.regs.rflags
}
fn set_flags(&mut self, flags: u64) {
self.regs.rflags = flags;
}
fn mode(&self) -> Result<CpuMode, PlatformError> {
let efer = self.efer();
let cr0 = self.read_reg(Register::CR0)?;
let mut mode = CpuMode::Real;
if (cr0 & CR0_PE) == CR0_PE {
mode = CpuMode::Protected;
}
if (efer & EFER_LMA) == EFER_LMA {
if mode != CpuMode::Protected {
return Err(PlatformError::InvalidState(anyhow!(
"Protection must be enabled in long mode"
)));
}
mode = CpuMode::Long;
}
Ok(mode)
}
}
pub struct Emulator<'a, T: CpuStateManager> {
platform: &'a mut dyn PlatformEmulator<CpuState = T>,
}
// Reduce repetition, see its invocation in get_handler().
macro_rules! gen_handler_match {
($value: ident, $( ($module:ident, $code:ident) ),* ) => {
match $value {
$(
Code::$code => Some(Box::new($module::$code)),
)*
_ => None,
}
};
}
impl<'a, T: CpuStateManager> Emulator<'a, T> {
pub fn new(platform: &mut dyn PlatformEmulator<CpuState = T>) -> Emulator<T> {
Emulator { platform }
}
fn get_handler(code: Code) -> Option<Box<dyn InstructionHandler<T>>> {
let handler: Option<Box<dyn InstructionHandler<T>>> = gen_handler_match!(
code,
// CMP
(cmp, Cmp_rm32_r32),
(cmp, Cmp_rm8_r8),
(cmp, Cmp_rm32_imm8),
(cmp, Cmp_rm64_r64),
// MOV
(mov, Mov_r8_rm8),
(mov, Mov_r8_imm8),
(mov, Mov_r16_imm16),
(mov, Mov_r16_rm16),
(mov, Mov_r32_imm32),
(mov, Mov_r32_rm32),
(mov, Mov_r64_imm64),
(mov, Mov_r64_rm64),
(mov, Mov_rm8_imm8),
(mov, Mov_rm8_r8),
(mov, Mov_rm16_imm16),
(mov, Mov_rm16_r16),
(mov, Mov_rm32_imm32),
(mov, Mov_rm32_r32),
(mov, Mov_rm64_imm32),
(mov, Mov_rm64_r64),
// MOVZX
(mov, Movzx_r16_rm8),
(mov, Movzx_r32_rm8),
(mov, Movzx_r64_rm8),
(mov, Movzx_r32_rm16),
(mov, Movzx_r64_rm16),
// MOVS
(movs, Movsd_m32_m32)
);
handler
}
fn emulate_insn_stream(
&mut self,
cpu_id: usize,
insn_stream: &[u8],
num_insn: Option<usize>,
) -> EmulationResult<T, Exception> {
let mut state = self
.platform
.cpu_state(cpu_id)
.map_err(EmulationError::PlatformEmulationError)?;
let mut decoder = Decoder::new(64, insn_stream, DecoderOptions::NONE);
let mut insn = Instruction::default();
let mut num_insn_emulated: usize = 0;
let mut fetched_insn_stream: [u8; 16] = [0; 16];
let mut last_decoded_ip: u64 = state.ip();
let mut stop_emulation: bool = false;
decoder.set_ip(state.ip());
while decoder.can_decode() && !stop_emulation {
decoder.decode_out(&mut insn);
if decoder.last_error() == DecoderError::NoMoreBytes {
// The decoder is missing some bytes to decode the current
// instruction, for example because the instruction stream
// crosses a page boundary.
// We fetch 16 more bytes from the instruction segment,
// decode and emulate the failing instruction and terminate
// the emulation loop.
debug!(
"Fetching {} bytes from {:#x}",
fetched_insn_stream.len(),
last_decoded_ip
);
// fetched_insn_stream is 16 bytes long, enough to contain
// any complete x86 instruction.
self.platform
.fetch(last_decoded_ip, &mut fetched_insn_stream)
.map_err(EmulationError::PlatformEmulationError)?;
debug!("Fetched {:x?}", fetched_insn_stream);
// Once we have the new stream, we must create a new decoder
// and emulate one last instruction from the last decoded IP.
decoder = Decoder::new(64, &fetched_insn_stream, DecoderOptions::NONE);
decoder.decode_out(&mut insn);
if decoder.last_error() != DecoderError::None {
return Err(EmulationError::InstructionFetchingError(anyhow!(
"{:#x?}",
insn_format!(insn)
)));
}
stop_emulation = true;
}
// Emulate the decoded instruction
Emulator::get_handler(insn.code())
.ok_or_else(|| {
EmulationError::UnsupportedInstruction(anyhow!(
"{:#x?} {:?} {:?}",
insn_format!(insn),
insn.mnemonic(),
insn.code()
))
})?
.emulate(&insn, &mut state, self.platform)
.context(anyhow!("Failed to emulate {:#x?}", insn_format!(insn)))?;
last_decoded_ip = decoder.ip();
num_insn_emulated += 1;
if let Some(num_insn) = num_insn {
if num_insn_emulated >= num_insn {
// Exit the decoding loop, do not decode the next instruction.
stop_emulation = true;
}
}
}
state.set_ip(decoder.ip());
Ok(state)
}
/// Emulate all instructions from the instructions stream.
pub fn emulate(&mut self, cpu_id: usize, insn_stream: &[u8]) -> EmulationResult<T, Exception> {
self.emulate_insn_stream(cpu_id, insn_stream, None)
}
/// Only emulate the first instruction from the stream.
///
/// This is useful for cases where we get readahead instruction stream
/// but implicitly must only emulate the first instruction, and then return
/// to the guest.
pub fn emulate_first_insn(
&mut self,
cpu_id: usize,
insn_stream: &[u8],
) -> EmulationResult<T, Exception> {
self.emulate_insn_stream(cpu_id, insn_stream, Some(1))
}
}
#[cfg(test)]
mod mock_vmm {
#![allow(unused_mut)]
extern crate env_logger;
use super::*;
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState as CpuState};
use crate::arch::x86::gdt::{gdt_entry, segment_from_gdt};
use crate::arch::x86::Exception;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub struct MockVmm {
memory: Vec<u8>,
state: Arc<Mutex<CpuState>>,
}
unsafe impl Sync for MockVmm {}
pub type MockResult = Result<(), EmulationError<Exception>>;
impl MockVmm {
pub fn new(ip: u64, regs: Vec<(Register, u64)>, memory: Option<(u64, &[u8])>) -> MockVmm {
let _ = env_logger::try_init();
let cs_reg = segment_from_gdt(gdt_entry(0xc09b, 0, 0xffffffff), 1);
let ds_reg = segment_from_gdt(gdt_entry(0xc093, 0, 0xffffffff), 2);
let es_reg = segment_from_gdt(gdt_entry(0xc093, 0, 0xffffffff), 3);
let mut initial_state = CpuState::default();
initial_state.set_ip(ip);
initial_state.write_segment(Register::CS, cs_reg).unwrap();
initial_state.write_segment(Register::DS, ds_reg).unwrap();
initial_state.write_segment(Register::ES, es_reg).unwrap();
for (reg, value) in regs {
initial_state.write_reg(reg, value).unwrap();
}
let mut vmm = MockVmm {
memory: vec![0; 8192],
state: Arc::new(Mutex::new(initial_state)),
};
if let Some(mem) = memory {
vmm.write_memory(mem.0, &mem.1).unwrap();
}
vmm
}
pub fn emulate_insn(
&mut self,
cpu_id: usize,
insn: &[u8],
num_insn: Option<usize>,
) -> MockResult {
let ip = self.cpu_state(cpu_id).unwrap().ip();
let mut emulator = Emulator::new(self);
let new_state = emulator.emulate_insn_stream(cpu_id, &insn, num_insn)?;
if num_insn.is_none() {
assert_eq!(ip + insn.len() as u64, new_state.ip());
}
self.set_cpu_state(cpu_id, new_state).unwrap();
Ok(())
}
pub fn emulate_first_insn(&mut self, cpu_id: usize, insn: &[u8]) -> MockResult {
self.emulate_insn(cpu_id, insn, Some(1))
}
}
impl PlatformEmulator for MockVmm {
type CpuState = CpuState;
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
debug!(
"Memory read {} bytes from [{:#x} -> {:#x}]",
data.len(),
gva,
gva
);
data.copy_from_slice(&self.memory[gva as usize..gva as usize + data.len()]);
Ok(())
}
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
debug!(
"Memory write {} bytes at [{:#x} -> {:#x}]",
data.len(),
gva,
gva
);
self.memory[gva as usize..gva as usize + data.len()].copy_from_slice(data);
Ok(())
}
fn cpu_state(&self, _cpu_id: usize) -> Result<CpuState, PlatformError> {
Ok(self.state.lock().unwrap().clone())
}
fn set_cpu_state(
&self,
_cpu_id: usize,
state: Self::CpuState,
) -> Result<(), PlatformError> {
*self.state.lock().unwrap() = state;
Ok(())
}
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
Ok(gva)
}
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
let rip = self
.state
.lock()
.unwrap()
.linearize(Register::CS, ip, false)?;
self.read_memory(rip, instruction_bytes)
}
}
}
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;
#[test]
// Emulate truncated instruction stream, which should cause a fetch.
//
// mov rax, 0x1000
// Test with a first instruction truncated.
fn test_fetch_first_instruction() {
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory = [
// Code at IP
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
// Padding
0x00, 0x00, 0x00, 0x00, 0x00, // Padding is all zeroes
// Data at IP + 0x10 (0x1234567812345678 in LE)
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
];
let insn = [
// First instruction is truncated
0x48, 0xc7, 0xc0, 0x00, // mov rax, 0x1000 -- Missing bytes: 0x00, 0x10, 0x00, 0x00,
];
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
let rax: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RAX)
.unwrap();
assert_eq!(rax, ip);
}
#[test]
// Emulate truncated instruction stream, which should cause a fetch.
//
// mov rax, 0x1000
// mov rbx, qword ptr [rax+10h]
// Test with a 2nd instruction truncated.
fn test_fetch_second_instruction() {
let target_rax: u64 = 0x1234567812345678;
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory = [
// Code at IP
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
// Padding
0x00, 0x00, 0x00, 0x00, 0x00, // Padding is all zeroes
// Data at IP + 0x10 (0x1234567812345678 in LE)
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
];
let insn = [
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
0x48, 0x8b, // Truncated mov rbx, qword ptr [rax+10h] -- missing [0x58, 0x10]
];
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
let rbx: u64 = vmm
.cpu_state(cpu_id)
.unwrap()
.read_reg(Register::RBX)
.unwrap();
assert_eq!(rbx, target_rax);
}
#[test]
// Emulate truncated instruction stream, which should cause a fetch.
//
// mov rax, 0x1000
// Test with a first instruction truncated and a bad fetched instruction.
// Verify that the instruction emulation returns an error.
fn test_fetch_bad_insn() {
let ip: u64 = 0x1000;
let cpu_id = 0;
let memory = [
// Code at IP
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff,
];
let insn = [
// First instruction is truncated
0x48, 0xc7, 0xc0, 0x00, // mov rax, 0x1000 -- Missing bytes: 0x00, 0x10, 0x00, 0x00,
];
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_err());
}
}

View File

@@ -8,7 +8,7 @@
// found in the LICENSE-BSD-3-Clause file.
// For GDT details see arch/x86/include/asm/segment.h
use hypervisor::x86_64::SegmentRegister;
use crate::x86_64::SegmentRegister;
/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {

View File

@@ -11,21 +11,12 @@
// Copyright © 2020, Microsoft Corporation
//
pub mod emulator;
pub mod gdt;
#[allow(non_upper_case_globals)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[allow(non_upper_case_globals)]
#[allow(unused)]
#[allow(
clippy::unreadable_literal,
clippy::redundant_static_lifetimes,
clippy::trivially_copy_pass_by_ref,
clippy::useless_transmute,
clippy::should_implement_trait,
clippy::transmute_ptr_to_ptr,
clippy::unreadable_literal,
clippy::redundant_static_lifetimes
)]
pub mod msr_index;
// MTRR constants
@@ -34,3 +25,107 @@ pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
// IOAPIC pins
pub const NUM_IOAPIC_PINS: usize = 24;
// X86 Exceptions
#[allow(dead_code, clippy::upper_case_acronyms)]
#[derive(Clone, Debug)]
pub enum Exception {
DE = 0, // Divide Error
DB = 1, // Debug Exception
BP = 3, // Breakpoint
OF = 4, // Overflow
BR = 5, // BOUND Range Exceeded
UD = 6, // Invalid/Undefined Opcode
NM = 7, // No Math Coprocessor
DF = 8, // Double Fault
TS = 10, // Invalid TSS
NP = 11, // Segment Not Present
SS = 12, // Stack Segment Fault
GP = 13, // General Protection
PF = 14, // Page Fault
MF = 16, // Math Fault
AC = 17, // Alignment Check
MC = 18, // Machine Check
XM = 19, // SIMD Floating-Point Exception
VE = 20, // Virtualization Exception
CP = 21, // Control Protection Exception
}
pub mod regs;
// Abstracted segment register ops.
// Each x86 hypervisor should implement those.
pub trait SegmentRegisterOps {
// Segment type
fn segment_type(&self) -> u8;
fn set_segment_type(&mut self, val: u8);
// Descriptor Privilege Level (DPL)
fn dpl(&self) -> u8;
fn set_dpl(&mut self, val: u8);
// Granularity
fn granularity(&self) -> u8;
fn set_granularity(&mut self, val: u8);
// Memory Presence
fn present(&self) -> u8;
fn set_present(&mut self, val: u8);
// Long mode
fn long(&self) -> u8;
fn set_long(&mut self, val: u8);
// Available for system use (AVL)
fn avl(&self) -> u8;
fn set_avl(&mut self, val: u8);
// Descriptor type (System or code/data)
fn desc_type(&self) -> u8;
fn set_desc_type(&mut self, val: u8);
// D/B
fn db(&self) -> u8;
fn set_db(&mut self, val: u8);
}
// Code segment
pub const CODE_SEGMENT_TYPE: u8 = 0x8;
// Read/Write or Read/Exec segment
pub const RWRX_SEGMENT_TYPE: u8 = 0x2;
// Expand down segment
pub const EXPAND_DOWN_SEGMENT_TYPE: u8 = 0x4;
pub fn segment_type_code(t: u8) -> bool {
t & CODE_SEGMENT_TYPE != 0
}
pub fn segment_type_ro(t: u8) -> bool {
t & !RWRX_SEGMENT_TYPE == 0
}
pub fn segment_type_expand_down(t: u8) -> bool {
!segment_type_code(t) && (t & EXPAND_DOWN_SEGMENT_TYPE != 0)
}
#[macro_export]
macro_rules! msr {
($msr:expr) => {
MsrEntry {
index: $msr,
data: 0x0,
..Default::default()
}
};
}
#[macro_export]
macro_rules! msr_data {
($msr:expr, $data:expr) => {
MsrEntry {
index: $msr,
data: $data,
..Default::default()
}
};
}

View File

@@ -0,0 +1,34 @@
//
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
// EFER (technically not a register) bits
pub const EFER_LMA: u64 = 0x400;
pub const EFER_LME: u64 = 0x100;
// CR0 bits
pub const CR0_PE: u64 = 0x1;
pub const CR0_PG: u64 = 0x80000000;
// CR4 bits
pub const CR4_PAE: u64 = 0x20;
pub const CR4_LA57: u64 = 0x1000;
// RFlags bits
pub const CF_SHIFT: usize = 0;
pub const PF_SHIFT: usize = 2;
pub const AF_SHIFT: usize = 4;
pub const ZF_SHIFT: usize = 6;
pub const SF_SHIFT: usize = 7;
pub const DF_SHIFT: usize = 10;
pub const OF_SHIFT: usize = 11;
pub const CF: u64 = 1 << CF_SHIFT;
pub const PF: u64 = 1 << PF_SHIFT;
pub const AF: u64 = 1 << AF_SHIFT;
pub const ZF: u64 = 1 << ZF_SHIFT;
pub const SF: u64 = 1 << SF_SHIFT;
pub const DF: u64 = 1 << DF_SHIFT;
pub const OF: u64 = 1 << OF_SHIFT;

View File

@@ -12,13 +12,19 @@
use crate::aarch64::VcpuInit;
#[cfg(target_arch = "aarch64")]
use crate::aarch64::{RegList, Register, StandardRegisters};
use crate::{CpuState, MpState};
#[cfg(target_arch = "x86_64")]
use crate::x86_64::{CpuId, LapicState};
#[cfg(target_arch = "x86_64")]
use crate::x86_64::{
CpuId, ExtendedControlRegisters, FpuState, LapicState, MsrEntries, SpecialRegisters,
StandardRegisters, VcpuEvents, Xsave,
ExtendedControlRegisters, FpuState, MsrEntries, SpecialRegisters, StandardRegisters, VcpuEvents,
};
use crate::CpuState;
#[cfg(feature = "kvm")]
use crate::MpState;
#[cfg(target_arch = "x86_64")]
use crate::Xsave;
#[cfg(feature = "mshv")]
use mshv_bindings::*;
use thiserror::Error;
#[derive(Error, Debug)]
@@ -151,10 +157,24 @@ pub enum HypervisorCpuError {
#[error("Failed to notify guest its clock was paused: {0}")]
NotifyGuestClockPaused(#[source] anyhow::Error),
///
/// Setting debug register error
///
#[error("Failed to set debug registers: {0}")]
SetDebugRegs(#[source] anyhow::Error),
///
/// Getting debug register error
///
#[error("Failed to get debug registers: {0}")]
GetDebugRegs(#[source] anyhow::Error),
///
/// Write to Guest Mem
///
#[error("Failed to write to Guest Mem at: {0}")]
GuestMemWrite(#[source] anyhow::Error),
/// Enabling HyperV SynIC error
///
#[error("Failed to enable HyperV SynIC")]
EnableHyperVSynIC(#[source] anyhow::Error),
EnableHyperVSyncIc(#[source] anyhow::Error),
///
/// Getting AArch64 core register error
///
@@ -180,6 +200,17 @@ pub enum HypervisorCpuError {
///
#[error("Failed to set system register: {0}")]
SetSysRegister(#[source] anyhow::Error),
///
/// GVA translation error
///
#[error("Failed to translate GVA: {0}")]
TranslateVirtualAddress(#[source] anyhow::Error),
///
/// Failed to initialize TDX on CPU
///
#[cfg(feature = "tdx")]
#[error("Failed to initialize TDX: {0}")]
InitializeTdx(#[source] std::io::Error),
}
#[derive(Debug)]
@@ -194,6 +225,7 @@ pub enum VmExit<'a> {
MmioWrite(u64 /* address */, &'a [u8]),
Ignore,
Reset,
Shutdown,
Hyperv,
}
@@ -271,10 +303,12 @@ pub trait Vcpu: Send + Sync {
/// Setup the model-specific registers (MSR) for this vCPU.
///
fn set_msrs(&self, msrs: &MsrEntries) -> Result<usize>;
#[cfg(feature = "kvm")]
///
/// Returns the vcpu's current "multiprocessing state".
///
fn get_mp_state(&self) -> Result<MpState>;
#[cfg(feature = "kvm")]
///
/// Sets the vcpu's current "multiprocessing state".
///
@@ -311,7 +345,7 @@ pub trait Vcpu: Send + Sync {
/// of the vcpu.
///
fn set_vcpu_events(&self, events: &VcpuEvents) -> Result<()>;
#[cfg(target_arch = "x86_64")]
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
///
/// Let the guest know that it has been paused, which prevents from
/// potential soft lockups when being resumed.
@@ -377,4 +411,14 @@ pub trait Vcpu: Send + Sync {
/// Triggers the running of the current virtual CPU returning an exit reason.
///
fn run(&self) -> std::result::Result<VmExit, HypervisorCpuError>;
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
///
/// Translate guest virtual address to guest physical address
///
fn translate_gva(&self, gva: u64, flags: u64) -> Result<(u64, hv_translate_gva_result)>;
///
/// Initialize TDX support on the vCPU
///
#[cfg(feature = "tdx")]
fn tdx_init(&self, hob_address: u64) -> Result<()>;
}

View File

@@ -9,17 +9,23 @@
//
use crate::vm::Vm;
#[cfg(target_arch = "x86_64")]
use crate::x86_64::{CpuId, MsrList};
use crate::x86_64::CpuId;
#[cfg(target_arch = "x86_64")]
use crate::x86_64::MsrList;
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
use kvm_ioctls::Cap;
use std::sync::Arc;
use thiserror::Error;
#[derive(Error, Debug)]
///
///
pub enum HypervisorError {
///
/// hypervisor creation error
///
#[error("Failed to create the hypervisor: {0}")]
HypervisorCreate(#[source] anyhow::Error),
///
/// Vm creation failure
///
@@ -78,19 +84,28 @@ pub trait Hypervisor: Send + Sync {
/// Return a hypervisor-agnostic Vm trait object
///
fn create_vm(&self) -> Result<Arc<dyn Vm>>;
#[cfg(feature = "kvm")]
///
/// Create a Vm of a specific type using the underlying hypervisor
/// Return a hypervisor-agnostic Vm trait object
///
fn create_vm_with_type(&self, vm_type: u64) -> Result<Arc<dyn Vm>>;
#[cfg(feature = "kvm")]
///
/// Returns the size of the memory mapping required to use the vcpu's structures
///
fn get_vcpu_mmap_size(&self) -> Result<usize>;
#[cfg(feature = "kvm")]
///
/// Gets the recommended maximum number of VCPUs per VM.
///
fn get_max_vcpus(&self) -> Result<usize>;
#[cfg(feature = "kvm")]
///
/// Gets the recommended number of VCPUs per VM.
///
fn get_nr_vcpus(&self) -> Result<usize>;
#[cfg(target_arch = "x86_64")]
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
///
/// Checks if a particular `Cap` is available.
///
@@ -100,6 +115,7 @@ pub trait Hypervisor: Send + Sync {
/// Get the supported CpuID
///
fn get_cpuid(&self) -> Result<CpuId>;
#[cfg(not(feature = "mshv"))]
///
/// Check particular extensions if any
///

View File

@@ -16,10 +16,10 @@ pub use crate::aarch64::{
use crate::cpu;
use crate::device;
use crate::hypervisor;
use crate::vec_with_array_field;
use crate::vm::{self, VmmOps};
#[cfg(target_arch = "aarch64")]
use crate::{arm64_core_reg_id, offset__of};
use arc_swap::ArcSwapOption;
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
use serde_derive::{Deserialize, Serialize};
use std::os::unix::io::{AsRawFd, RawFd};
@@ -33,47 +33,45 @@ use vmm_sys_util::eventfd::EventFd;
// x86_64 dependencies
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::NUM_IOAPIC_PINS;
#[cfg(target_arch = "aarch64")]
use aarch64::{RegList, Register, StandardRegisters};
#[cfg(target_arch = "x86_64")]
use kvm_bindings::{
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
};
#[cfg(target_arch = "x86_64")]
use x86_64::{
check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters, KVM_TSS_ADDRESS,
};
#[cfg(target_arch = "aarch64")]
use aarch64::{RegList, Register, StandardRegisters};
#[cfg(target_arch = "x86_64")]
pub use x86_64::{
CpuId, CpuIdEntry, ExtendedControlRegisters, LapicState, MsrEntries, VcpuKvmState as CpuState,
Xsave, CPUID_FLAG_VALID_INDEX,
};
#[cfg(target_arch = "x86_64")]
use kvm_bindings::{
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
};
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::NUM_IOAPIC_PINS;
// aarch64 dependencies
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
pub use kvm_bindings;
#[cfg(feature = "tdx")]
use kvm_bindings::KVMIO;
pub use kvm_bindings::{
kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO, kvm_irq_routing, kvm_irq_routing_entry,
kvm_userspace_memory_region, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI,
KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_READONLY, KVM_MSI_VALID_DEVID,
};
#[cfg(target_arch = "aarch64")]
use kvm_bindings::{
kvm_regs, user_fpsimd_state, user_pt_regs, KVM_NR_SPSR, KVM_REG_ARM64, KVM_REG_ARM_CORE,
KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
};
#[cfg(target_arch = "aarch64")]
use std::mem;
pub use kvm_bindings;
pub use kvm_bindings::{
kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO, kvm_irq_routing, kvm_irq_routing_entry,
kvm_userspace_memory_region, KVM_IRQ_ROUTING_MSI, KVM_MEM_READONLY, KVM_MSI_VALID_DEVID,
};
pub use kvm_ioctls;
pub use kvm_ioctls::{Cap, Kvm};
#[cfg(target_arch = "aarch64")]
use std::mem;
#[cfg(feature = "tdx")]
use vmm_sys_util::{ioctl::ioctl_with_val, ioctl_expr, ioctl_ioc_nr, ioctl_iowr_nr};
///
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
@@ -86,6 +84,21 @@ pub use {
kvm_bindings::kvm_vcpu_events as VcpuEvents, kvm_ioctls::DeviceFd, kvm_ioctls::IoEventAddress,
kvm_ioctls::VcpuExit,
};
#[cfg(feature = "tdx")]
ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong);
#[cfg(feature = "tdx")]
#[repr(u32)]
enum TdxCommand {
#[allow(dead_code)]
Capabilities = 0,
InitVm,
InitVcpu,
InitMemRegion,
Finalize,
}
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
pub struct KvmVmState {}
@@ -96,37 +109,6 @@ pub struct KvmVm {
#[cfg(target_arch = "x86_64")]
msrs: MsrEntries,
state: KvmVmState,
vmmops: ArcSwapOption<Box<dyn vm::VmmOps>>,
}
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
fn vec_with_size_in_bytes<T: Default>(size_in_bytes: usize) -> Vec<T> {
let rounded_size = (size_in_bytes + size_of::<T>() - 1) / size_of::<T>();
let mut v = Vec::with_capacity(rounded_size);
v.resize_with(rounded_size, T::default);
v
}
// The kvm API has many structs that resemble the following `Foo` structure:
//
// ```
// #[repr(C)]
// struct Foo {
// some_data: u32
// entries: __IncompleteArrayField<__u32>,
// }
// ```
//
// In order to allocate such a structure, `size_of::<Foo>()` would be too small because it would not
// include any space for `entries`. To make the allocation large enough while still being aligned
// for `Foo`, a `Vec<Foo>` is created. Only the first element of `Vec<Foo>` would actually be used
// as a `Foo`. The remaining memory in the `Vec<Foo>` is for `entries`, which must be contiguous
// with `Foo`. This function is used to make the `Vec<Foo>` with enough space for `count` entries.
use std::mem::size_of;
fn vec_with_array_field<T: Default, F>(count: usize) -> Vec<T> {
let element_space = count * size_of::<F>();
let vec_size_bytes = size_of::<T>() + element_space;
vec_with_size_in_bytes(vec_size_bytes)
}
///
@@ -176,16 +158,20 @@ impl vm::Vm for KvmVm {
///
/// Creates a VcpuFd object from a vcpu RawFd.
///
fn create_vcpu(&self, id: u8) -> vm::Result<Arc<dyn cpu::Vcpu>> {
fn create_vcpu(
&self,
id: u8,
vmmops: Option<Arc<Box<dyn VmmOps>>>,
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
let vc = self
.fd
.create_vcpu(id)
.create_vcpu(id as u64)
.map_err(|e| vm::HypervisorVmError::CreateVcpu(e.into()))?;
let vcpu = KvmVcpu {
fd: vc,
#[cfg(target_arch = "x86_64")]
msrs: self.msrs.clone(),
vmmops: self.vmmops.clone(),
vmmops,
#[cfg(target_arch = "x86_64")]
hyperv_synic: AtomicBool::new(false),
};
@@ -255,13 +241,19 @@ impl vm::Vm for KvmVm {
memory_size: u64,
userspace_addr: u64,
readonly: bool,
log_dirty_pages: bool,
) -> MemoryRegion {
MemoryRegion {
slot,
guest_phys_addr,
memory_size,
userspace_addr,
flags: if readonly { KVM_MEM_READONLY } else { 0 },
flags: if readonly { KVM_MEM_READONLY } else { 0 }
| if log_dirty_pages {
KVM_MEM_LOG_DIRTY_PAGES
} else {
0
},
}
}
///
@@ -305,8 +297,10 @@ impl vm::Vm for KvmVm {
// Create split irqchip
// Only the local APIC is emulated in kernel, both PICs and IOAPIC
// are not.
let mut cap: kvm_enable_cap = Default::default();
cap.cap = KVM_CAP_SPLIT_IRQCHIP;
let mut cap = kvm_enable_cap {
cap: KVM_CAP_SPLIT_IRQCHIP,
..Default::default()
};
cap.args[0] = NUM_IOAPIC_PINS as u64;
self.fd
.enable_cap(&cap)
@@ -356,13 +350,116 @@ impl vm::Vm for KvmVm {
}
///
/// Set the VmmOps interface
/// Get dirty pages bitmap (one bit per page)
///
fn set_vmmops(&self, vmmops: Box<dyn VmmOps>) -> vm::Result<()> {
self.vmmops.store(Some(Arc::new(vmmops)));
Ok(())
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> vm::Result<Vec<u64>> {
self.fd
.get_dirty_log(slot, memory_size as usize)
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
}
///
/// Initialize TDX for this VM
///
#[cfg(feature = "tdx")]
fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> vm::Result<()> {
#[repr(C)]
struct TdxInitVm {
max_vcpus: u32,
reserved: u32,
attributes: u64,
cpuid: u64,
}
let data = TdxInitVm {
max_vcpus,
reserved: 0,
attributes: 0,
cpuid: cpuid.as_fam_struct_ptr() as u64,
};
tdx_command(
&self.fd.as_raw_fd(),
TdxCommand::InitVm,
0,
&data as *const _ as u64,
)
.map_err(vm::HypervisorVmError::InitializeTdx)
}
///
/// Finalize the TDX setup for this VM
///
#[cfg(feature = "tdx")]
fn tdx_finalize(&self) -> vm::Result<()> {
tdx_command(&self.fd.as_raw_fd(), TdxCommand::Finalize, 0, 0)
.map_err(vm::HypervisorVmError::FinalizeTdx)
}
///
/// Initialize memory regions for the TDX VM
///
#[cfg(feature = "tdx")]
fn tdx_init_memory_region(
&self,
host_address: u64,
guest_address: u64,
size: u64,
measure: bool,
) -> vm::Result<()> {
#[repr(C)]
struct TdxInitMemRegion {
host_address: u64,
guest_address: u64,
pages: u64,
}
let data = TdxInitMemRegion {
host_address,
guest_address,
pages: size / 4096,
};
tdx_command(
&self.fd.as_raw_fd(),
TdxCommand::InitMemRegion,
if measure { 1 } else { 0 },
&data as *const _ as u64,
)
.map_err(vm::HypervisorVmError::InitMemRegionTdx)
}
}
#[cfg(feature = "tdx")]
fn tdx_command(
fd: &RawFd,
command: TdxCommand,
metadata: u32,
data: u64,
) -> std::result::Result<(), std::io::Error> {
#[repr(C)]
struct TdxIoctlCmd {
command: TdxCommand,
metadata: u32,
data: u64,
}
let cmd = TdxIoctlCmd {
command,
metadata,
data,
};
let ret = unsafe {
ioctl_with_val(
fd,
KVM_MEMORY_ENCRYPT_OP(),
&cmd as *const TdxIoctlCmd as std::os::raw::c_ulong,
)
};
if ret < 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}
/// Wrapper over KVM system ioctls.
pub struct KvmHypervisor {
kvm: Kvm,
@@ -395,18 +492,18 @@ impl KvmHypervisor {
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
///
impl hypervisor::Hypervisor for KvmHypervisor {
/// Create a KVM vm object and return the object as Vm trait object
/// Create a KVM vm object of a specific VM type and return the object as Vm trait object
/// Example
/// # extern crate hypervisor;
/// # use hypervisor::KvmHypervisor;
/// use hypervisor::KvmVm;
/// let hypervisor = KvmHypervisor::new().unwrap();
/// let vm = hypervisor.create_vm().unwrap()
/// let vm = hypervisor.create_vm_with_type(KvmVmType::LegacyVm).unwrap()
///
fn create_vm(&self) -> hypervisor::Result<Arc<dyn vm::Vm>> {
fn create_vm_with_type(&self, vm_type: u64) -> hypervisor::Result<Arc<dyn vm::Vm>> {
let fd: VmFd;
loop {
match self.kvm.create_vm() {
match self.kvm.create_vm_with_type(vm_type) {
Ok(res) => fd = res,
Err(e) => {
if e.errno() == libc::EINTR {
@@ -428,7 +525,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
{
let msr_list = self.get_msr_list()?;
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
let mut msrs = MsrEntries::new(num_msrs);
let mut msrs = MsrEntries::new(num_msrs).unwrap();
let indices = msr_list.as_slice();
let msr_entries = msrs.as_mut_slice();
for (pos, index) in indices.iter().enumerate() {
@@ -439,7 +536,6 @@ impl hypervisor::Hypervisor for KvmHypervisor {
fd: vm_fd,
msrs,
state: VmState {},
vmmops: ArcSwapOption::from(None),
}))
}
@@ -448,11 +544,22 @@ impl hypervisor::Hypervisor for KvmHypervisor {
Ok(Arc::new(KvmVm {
fd: vm_fd,
state: VmState {},
vmmops: ArcSwapOption::from(None),
}))
}
}
/// Create a KVM vm object and return the object as Vm trait object
/// Example
/// # extern crate hypervisor;
/// # use hypervisor::KvmHypervisor;
/// use hypervisor::KvmVm;
/// let hypervisor = KvmHypervisor::new().unwrap();
/// let vm = hypervisor.create_vm().unwrap()
///
fn create_vm(&self) -> hypervisor::Result<Arc<dyn vm::Vm>> {
self.create_vm_with_type(0) // Create with default platform type
}
fn check_required_extensions(&self) -> hypervisor::Result<()> {
check_required_kvm_extensions(&self.kvm).expect("Missing KVM capabilities");
Ok(())
@@ -509,7 +616,7 @@ pub struct KvmVcpu {
fd: VcpuFd,
#[cfg(target_arch = "x86_64")]
msrs: MsrEntries,
vmmops: ArcSwapOption<Box<dyn vm::VmmOps>>,
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
#[cfg(target_arch = "x86_64")]
hyperv_synic: AtomicBool,
}
@@ -520,7 +627,7 @@ pub struct KvmVcpu {
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
/// let vcpu = vm.create_vcpu(0).unwrap();
/// let vcpu = vm.create_vcpu(0, None).unwrap();
/// vcpu.get/set().unwrap()
///
impl cpu::Vcpu for KvmVcpu {
@@ -594,13 +701,15 @@ impl cpu::Vcpu for KvmVcpu {
fn enable_hyperv_synic(&self) -> cpu::Result<()> {
// Update the information about Hyper-V SynIC being enabled and
// emulated as it will influence later which MSRs should be saved.
self.hyperv_synic.store(true, Ordering::SeqCst);
self.hyperv_synic.store(true, Ordering::Release);
let mut cap: kvm_enable_cap = Default::default();
cap.cap = KVM_CAP_HYPERV_SYNIC;
let cap = kvm_enable_cap {
cap: KVM_CAP_HYPERV_SYNIC,
..Default::default()
};
self.fd
.enable_cap(&cap)
.map_err(|e| cpu::HypervisorCpuError::EnableHyperVSynIC(e.into()))
.map_err(|e| cpu::HypervisorCpuError::EnableHyperVSyncIc(e.into()))
}
///
/// X86 specific call to retrieve the CPUID registers.
@@ -708,7 +817,7 @@ impl cpu::Vcpu for KvmVcpu {
Ok(run) => match run {
#[cfg(target_arch = "x86_64")]
VcpuExit::IoIn(addr, data) => {
if let Some(vmmops) = self.vmmops.load_full() {
if let Some(vmmops) = &self.vmmops {
return vmmops
.pio_read(addr.into(), data)
.map(|_| cpu::VmExit::Ignore)
@@ -719,7 +828,7 @@ impl cpu::Vcpu for KvmVcpu {
}
#[cfg(target_arch = "x86_64")]
VcpuExit::IoOut(addr, data) => {
if let Some(vmmops) = self.vmmops.load_full() {
if let Some(vmmops) = &self.vmmops {
return vmmops
.pio_write(addr.into(), data)
.map(|_| cpu::VmExit::Ignore)
@@ -738,10 +847,10 @@ impl cpu::Vcpu for KvmVcpu {
use kvm_bindings::{KVM_SYSTEM_EVENT_RESET, KVM_SYSTEM_EVENT_SHUTDOWN};
// On Aarch64, when the VM is shutdown, run() returns
// VcpuExit::SystemEvent with reason KVM_SYSTEM_EVENT_SHUTDOWN
if event_type == KVM_SYSTEM_EVENT_SHUTDOWN
|| event_type == KVM_SYSTEM_EVENT_RESET
{
if event_type == KVM_SYSTEM_EVENT_RESET {
Ok(cpu::VmExit::Reset)
} else if event_type == KVM_SYSTEM_EVENT_SHUTDOWN {
Ok(cpu::VmExit::Shutdown)
} else {
Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
"Unexpected system event with type 0x{:x}, flags 0x{:x}",
@@ -752,7 +861,7 @@ impl cpu::Vcpu for KvmVcpu {
}
VcpuExit::MmioRead(addr, data) => {
if let Some(vmmops) = self.vmmops.load_full() {
if let Some(vmmops) = &self.vmmops {
return vmmops
.mmio_read(addr, data)
.map(|_| cpu::VmExit::Ignore)
@@ -762,7 +871,7 @@ impl cpu::Vcpu for KvmVcpu {
Ok(cpu::VmExit::MmioRead(addr, data))
}
VcpuExit::MmioWrite(addr, data) => {
if let Some(vmmops) = self.vmmops.load_full() {
if let Some(vmmops) = &self.vmmops {
return vmmops
.mmio_write(addr, data)
.map(|_| cpu::VmExit::Ignore)
@@ -1031,7 +1140,7 @@ impl cpu::Vcpu for KvmVcpu {
fn system_registers(&self, state: &mut Vec<Register>) -> cpu::Result<()> {
// Call KVM_GET_REG_LIST to get all registers available to the guest. For ArmV8 there are
// around 500 registers.
let mut reg_list = RegList::new(512);
let mut reg_list = RegList::new(500).unwrap();
self.fd
.get_reg_list(&mut reg_list)
.map_err(|e| cpu::HypervisorCpuError::GetRegList(e.into()))?;
@@ -1121,7 +1230,7 @@ impl cpu::Vcpu for KvmVcpu {
/// let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
/// let vm = hv.create_vm().expect("new VM fd creation failed");
/// vm.enable_split_irq().unwrap();
/// let vcpu = vm.create_vcpu(0).unwrap();
/// let vcpu = vm.create_vcpu(0, None).unwrap();
/// let state = vcpu.state().unwrap();
/// ```
fn state(&self) -> cpu::Result<CpuState> {
@@ -1143,7 +1252,7 @@ impl cpu::Vcpu for KvmVcpu {
// Save extra MSRs if the Hyper-V synthetic interrupt controller is
// emulated.
if self.hyperv_synic.load(Ordering::SeqCst) {
if self.hyperv_synic.load(Ordering::Acquire) {
let hyperv_synic_msrs = vec![
0x40000020, 0x40000021, 0x40000080, 0x40000081, 0x40000082, 0x40000083, 0x40000084,
0x40000090, 0x40000091, 0x40000092, 0x40000093, 0x40000094, 0x40000095, 0x40000096,
@@ -1165,7 +1274,7 @@ impl cpu::Vcpu for KvmVcpu {
let msrs = if num_msrs != expected_num_msrs {
let mut faulty_msr_index = num_msrs;
let mut msr_entries_tmp =
MsrEntries::from_entries(&msr_entries.as_slice()[..faulty_msr_index]);
MsrEntries::from_entries(&msr_entries.as_slice()[..faulty_msr_index]).unwrap();
loop {
warn!(
@@ -1175,7 +1284,7 @@ impl cpu::Vcpu for KvmVcpu {
let start_pos = faulty_msr_index + 1;
let mut sub_msr_entries =
MsrEntries::from_entries(&msr_entries.as_slice()[start_pos..]);
MsrEntries::from_entries(&msr_entries.as_slice()[start_pos..]).unwrap();
let expected_num_msrs = sub_msr_entries.as_fam_struct_ref().nmsrs as usize;
let num_msrs = self.get_msrs(&mut sub_msr_entries)?;
@@ -1222,12 +1331,13 @@ impl cpu::Vcpu for KvmVcpu {
///
#[cfg(target_arch = "aarch64")]
fn state(&self) -> cpu::Result<CpuState> {
let mut state = CpuState::default();
// Get this vCPUs multiprocessing state.
state.mp_state = self.get_mp_state()?;
let mut state = CpuState {
mp_state: self.get_mp_state()?,
mpidr: self.read_mpidr()?,
..Default::default()
};
self.core_registers(&mut state.core_regs)?;
self.system_registers(&mut state.sys_regs)?;
state.mpidr = self.read_mpidr()?;
Ok(state)
}
@@ -1267,7 +1377,7 @@ impl cpu::Vcpu for KvmVcpu {
/// let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
/// let vm = hv.create_vm().expect("new VM fd creation failed");
/// vm.enable_split_irq().unwrap();
/// let vcpu = vm.create_vcpu(0).unwrap();
/// let vcpu = vm.create_vcpu(0, None).unwrap();
/// let state = vcpu.state().unwrap();
/// vcpu.set_state(&state).unwrap();
/// ```
@@ -1298,7 +1408,8 @@ impl cpu::Vcpu for KvmVcpu {
);
let start_pos = faulty_msr_index + 1;
let sub_msr_entries = MsrEntries::from_entries(&state.msrs.as_slice()[start_pos..]);
let sub_msr_entries =
MsrEntries::from_entries(&state.msrs.as_slice()[start_pos..]).unwrap();
let expected_num_msrs = sub_msr_entries.as_fam_struct_ref().nmsrs as usize;
let num_msrs = self.set_msrs(&sub_msr_entries)?;
@@ -1325,6 +1436,15 @@ impl cpu::Vcpu for KvmVcpu {
Ok(())
}
///
/// Initialize TDX for this CPU
///
#[cfg(feature = "tdx")]
fn tdx_init(&self, hob_address: u64) -> cpu::Result<()> {
tdx_command(&self.fd.as_raw_fd(), TdxCommand::InitVcpu, 0, hob_address)
.map_err(cpu::HypervisorCpuError::InitializeTdx)
}
}
/// Device struct for KVM

View File

@@ -8,11 +8,10 @@
//
//
use vm_memory::GuestAddress;
use crate::arch::x86::{msr_index, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
use serde_derive::{Deserialize, Serialize};
use vm_memory::GuestAddress;
///
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
@@ -28,27 +27,73 @@ pub use {
kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX as CPUID_FLAG_VALID_INDEX,
};
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
impl SegmentRegisterOps for SegmentRegister {
fn segment_type(&self) -> u8 {
self.type_
}
fn set_segment_type(&mut self, val: u8) {
self.type_ = val;
}
macro_rules! msr {
($msr:expr) => {
MsrEntry {
index: $msr,
data: 0x0,
..Default::default()
}
};
}
macro_rules! msr_data {
($msr:expr, $data:expr) => {
MsrEntry {
index: $msr,
data: $data,
..Default::default()
}
};
fn dpl(&self) -> u8 {
self.dpl
}
fn set_dpl(&mut self, val: u8) {
self.dpl = val;
}
fn present(&self) -> u8 {
self.present
}
fn set_present(&mut self, val: u8) {
self.present = val;
}
fn long(&self) -> u8 {
self.l
}
fn set_long(&mut self, val: u8) {
self.l = val;
}
fn avl(&self) -> u8 {
self.avl
}
fn set_avl(&mut self, val: u8) {
self.avl = val;
}
fn desc_type(&self) -> u8 {
self.s
}
fn set_desc_type(&mut self, val: u8) {
self.s = val;
}
fn granularity(&self) -> u8 {
self.g
}
fn set_granularity(&mut self, val: u8) {
self.g = val;
}
fn db(&self) -> u8 {
self.db
}
fn set_db(&mut self, val: u8) {
self.db = val;
}
}
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
pub fn boot_msr_entries() -> MsrEntries {
MsrEntries::from_entries(&[
msr!(msr_index::MSR_IA32_SYSENTER_CS),
@@ -66,6 +111,7 @@ pub fn boot_msr_entries() -> MsrEntries {
),
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
])
.unwrap()
}
///

View File

@@ -28,18 +28,24 @@ extern crate serde_derive;
extern crate serde_json;
extern crate thiserror;
/// Architecture specific definitions
#[macro_use]
pub mod arch;
#[cfg(feature = "kvm")]
/// KVM implementation module
pub mod kvm;
/// Microsoft Hypervisor implementation module
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
pub mod mshv;
/// Hypevisor related module
pub mod hypervisor;
/// Vm related module
pub mod vm;
/// Architecture specific definitions
pub mod arch;
/// CPU related module
mod cpu;
@@ -49,7 +55,10 @@ mod device;
pub use crate::hypervisor::{Hypervisor, HypervisorError};
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
pub use device::{Device, HypervisorDeviceError};
#[cfg(feature = "kvm")]
pub use kvm::*;
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
pub use mshv::*;
pub use vm::{DataMatch, HypervisorVmError, Vm};
use std::sync::Arc;
@@ -58,5 +67,38 @@ pub fn new() -> std::result::Result<Arc<dyn Hypervisor>, HypervisorError> {
#[cfg(feature = "kvm")]
let hv = kvm::KvmHypervisor::new()?;
#[cfg(feature = "mshv")]
let hv = mshv::MshvHypervisor::new()?;
Ok(Arc::new(hv))
}
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
fn vec_with_size_in_bytes<T: Default>(size_in_bytes: usize) -> Vec<T> {
let rounded_size = (size_in_bytes + size_of::<T>() - 1) / size_of::<T>();
let mut v = Vec::with_capacity(rounded_size);
v.resize_with(rounded_size, T::default);
v
}
// The kvm API has many structs that resemble the following `Foo` structure:
//
// ```
// #[repr(C)]
// struct Foo {
// some_data: u32
// entries: __IncompleteArrayField<__u32>,
// }
// ```
//
// In order to allocate such a structure, `size_of::<Foo>()` would be too small because it would not
// include any space for `entries`. To make the allocation large enough while still being aligned
// for `Foo`, a `Vec<Foo>` is created. Only the first element of `Vec<Foo>` would actually be used
// as a `Foo`. The remaining memory in the `Vec<Foo>` is for `entries`, which must be contiguous
// with `Foo`. This function is used to make the `Vec<Foo>` with enough space for `count` entries.
use std::mem::size_of;
pub fn vec_with_array_field<T: Default, F>(count: usize) -> Vec<T> {
let element_space = count * size_of::<F>();
let vec_size_bytes = size_of::<T>() + element_space;
vec_with_size_in_bytes(vec_size_bytes)
}

869
hypervisor/src/mshv/mod.rs Normal file
View File

@@ -0,0 +1,869 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
use crate::arch::emulator::{PlatformEmulator, PlatformError};
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState};
use crate::cpu;
use crate::cpu::Vcpu;
use crate::hypervisor;
use crate::vec_with_array_field;
use crate::vm::{self, VmmOps};
pub use mshv_bindings::*;
pub use mshv_ioctls::IoEventAddress;
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd};
use serde_derive::{Deserialize, Serialize};
use std::sync::Arc;
use vm::DataMatch;
// x86_64 dependencies
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
use crate::device;
use vmm_sys_util::eventfd::EventFd;
#[cfg(target_arch = "x86_64")]
pub use x86_64::VcpuMshvState as CpuState;
#[cfg(target_arch = "x86_64")]
pub use x86_64::*;
use std::os::unix::io::AsRawFd;
use std::sync::RwLock;
pub const PAGE_SHIFT: usize = 12;
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
pub struct HvState {
hypercall_page: u64,
}
pub use HvState as VmState;
/// Wrapper over mshv system ioctls.
pub struct MshvHypervisor {
mshv: Mshv,
}
impl MshvHypervisor {
/// Create a hypervisor based on Mshv
pub fn new() -> hypervisor::Result<MshvHypervisor> {
let mshv_obj =
Mshv::new().map_err(|e| hypervisor::HypervisorError::HypervisorCreate(e.into()))?;
Ok(MshvHypervisor { mshv: mshv_obj })
}
}
/// Implementation of Hypervisor trait for Mshv
/// Example:
/// #[cfg(feature = "mshv")]
/// extern crate hypervisor
/// let mshv = hypervisor::mshv::MshvHypervisor::new().unwrap();
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(mshv);
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
///
impl hypervisor::Hypervisor for MshvHypervisor {
/// Create a mshv vm object and return the object as Vm trait object
/// Example
/// # extern crate hypervisor;
/// # use hypervisor::MshvHypervisor;
/// use hypervisor::MshvVm;
/// let hypervisor = MshvHypervisor::new().unwrap();
/// let vm = hypervisor.create_vm().unwrap()
///
fn create_vm(&self) -> hypervisor::Result<Arc<dyn vm::Vm>> {
let fd: VmFd;
loop {
match self.mshv.create_vm() {
Ok(res) => fd = res,
Err(e) => {
if e.errno() == libc::EINTR {
// If the error returned is EINTR, which means the
// ioctl has been interrupted, we have to retry as
// this can't be considered as a regular error.
continue;
} else {
return Err(hypervisor::HypervisorError::VmCreate(e.into()));
}
}
}
break;
}
let msr_list = self.get_msr_list()?;
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
let mut msrs = MsrEntries::new(num_msrs).unwrap();
let indices = msr_list.as_slice();
let msr_entries = msrs.as_mut_slice();
for (pos, index) in indices.iter().enumerate() {
msr_entries[pos].index = *index;
}
let vm_fd = Arc::new(fd);
Ok(Arc::new(MshvVm {
fd: vm_fd,
msrs,
hv_state: hv_state_init(),
vmmops: None,
}))
}
///
/// Get the supported CpuID
///
fn get_cpuid(&self) -> hypervisor::Result<CpuId> {
Ok(CpuId::new(1).unwrap())
}
#[cfg(target_arch = "x86_64")]
///
/// Retrieve the list of MSRs supported by KVM.
///
fn get_msr_list(&self) -> hypervisor::Result<MsrList> {
self.mshv
.get_msr_index_list()
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
}
}
#[allow(dead_code)]
/// Vcpu struct for Microsoft Hypervisor
pub struct MshvVcpu {
fd: VcpuFd,
vp_index: u8,
cpuid: CpuId,
msrs: MsrEntries,
hv_state: Arc<RwLock<HvState>>, // Mshv State
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
}
/// Implementation of Vcpu trait for Microsoft Hypervisor
/// Example:
/// #[cfg(feature = "mshv")]
/// extern crate hypervisor
/// let mshv = hypervisor::mshv::MshvHypervisor::new().unwrap();
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(mshv);
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
/// let vcpu = vm.create_vcpu(0).unwrap();
/// vcpu.get/set().unwrap()
///
impl cpu::Vcpu for MshvVcpu {
#[cfg(target_arch = "x86_64")]
///
/// Returns the vCPU general purpose registers.
///
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
self.fd
.get_regs()
.map_err(|e| cpu::HypervisorCpuError::GetStandardRegs(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Sets the vCPU general purpose registers.
///
fn set_regs(&self, regs: &StandardRegisters) -> cpu::Result<()> {
self.fd
.set_regs(regs)
.map_err(|e| cpu::HypervisorCpuError::SetStandardRegs(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Returns the vCPU special registers.
///
fn get_sregs(&self) -> cpu::Result<SpecialRegisters> {
self.fd
.get_sregs()
.map_err(|e| cpu::HypervisorCpuError::GetSpecialRegs(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Sets the vCPU special registers.
///
fn set_sregs(&self, sregs: &SpecialRegisters) -> cpu::Result<()> {
self.fd
.set_sregs(sregs)
.map_err(|e| cpu::HypervisorCpuError::SetSpecialRegs(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Returns the floating point state (FPU) from the vCPU.
///
fn get_fpu(&self) -> cpu::Result<FpuState> {
self.fd
.get_fpu()
.map_err(|e| cpu::HypervisorCpuError::GetFloatingPointRegs(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Set the floating point state (FPU) of a vCPU.
///
fn set_fpu(&self, fpu: &FpuState) -> cpu::Result<()> {
self.fd
.set_fpu(fpu)
.map_err(|e| cpu::HypervisorCpuError::SetFloatingPointRegs(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Returns the model-specific registers (MSR) for this vCPU.
///
fn get_msrs(&self, msrs: &mut MsrEntries) -> cpu::Result<usize> {
self.fd
.get_msrs(msrs)
.map_err(|e| cpu::HypervisorCpuError::GetMsrEntries(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Setup the model-specific registers (MSR) for this vCPU.
/// Returns the number of MSR entries actually written.
///
fn set_msrs(&self, msrs: &MsrEntries) -> cpu::Result<usize> {
self.fd
.set_msrs(msrs)
.map_err(|e| cpu::HypervisorCpuError::SetMsrEntries(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that returns the vcpu's current "xcrs".
///
fn get_xcrs(&self) -> cpu::Result<ExtendedControlRegisters> {
self.fd
.get_xcrs()
.map_err(|e| cpu::HypervisorCpuError::GetXcsr(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that sets the vcpu's current "xcrs".
///
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> {
self.fd
.set_xcrs(&xcrs)
.map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Returns currently pending exceptions, interrupts, and NMIs as well as related
/// states of the vcpu.
///
fn get_vcpu_events(&self) -> cpu::Result<VcpuEvents> {
self.fd
.get_vcpu_events()
.map_err(|e| cpu::HypervisorCpuError::GetVcpuEvents(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Sets pending exceptions, interrupts, and NMIs as well as related states
/// of the vcpu.
///
fn set_vcpu_events(&self, events: &VcpuEvents) -> cpu::Result<()> {
self.fd
.set_vcpu_events(events)
.map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call to enable HyperV SynIC
///
fn enable_hyperv_synic(&self) -> cpu::Result<()> {
/* We always have SynIC enabled on MSHV */
Ok(())
}
#[allow(non_upper_case_globals)]
fn run(&self) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
// Safe because this is just only done during initialization.
// TODO don't zero it everytime we enter this function.
let hv_message: hv_message = unsafe { std::mem::zeroed() };
match self.fd.run(hv_message) {
Ok(x) => match x.header.message_type {
hv_message_type_HVMSG_X64_HALT => {
debug!("HALT");
Ok(cpu::VmExit::Reset)
}
hv_message_type_HVMSG_UNRECOVERABLE_EXCEPTION => {
warn!("TRIPLE FAULT");
Ok(cpu::VmExit::Shutdown)
}
hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT => {
let info = x.to_ioport_info().unwrap();
let access_info = info.access_info;
let len = unsafe { access_info.__bindgen_anon_1.access_size() } as usize;
let is_write = info.header.intercept_access_type == 1;
let port = info.port_number;
let mut data: [u8; 4] = [0; 4];
let mut ret_rax = info.rax;
/*
* XXX: Ignore QEMU fw_cfg (0x5xx) and debug console (0x402) ports.
*
* Cloud Hypervisor doesn't support fw_cfg at the moment. It does support 0x402
* under the "fwdebug" feature flag. But that feature is not enabled by default
* and is considered legacy.
*
* OVMF unconditionally pokes these IO ports with string IO.
*
* Instead of trying to implement string IO support now which does not do much
* now, skip those ports explicitly to avoid panicking.
*
* Proper string IO support can be added once we gain the ability to translate
* guest virtual addresses to guest physical addresses on MSHV.
*/
match port {
0x402 | 0x510 | 0x511 | 0x514 => {
let insn_len = info.header.instruction_length() as u64;
/* Advance RIP and update RAX */
let arr_reg_name_value = [
(
hv_register_name::HV_X64_REGISTER_RIP,
info.header.rip + insn_len,
),
(hv_register_name::HV_X64_REGISTER_RAX, ret_rax),
];
set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
return Ok(cpu::VmExit::Ignore);
}
_ => {}
}
if unsafe { access_info.__bindgen_anon_1.string_op() } == 1 {
panic!("String IN/OUT not supported");
}
if unsafe { access_info.__bindgen_anon_1.rep_prefix() } == 1 {
panic!("Rep IN/OUT not supported");
}
if is_write {
let data = (info.rax as u32).to_le_bytes();
if let Some(vmmops) = &self.vmmops {
vmmops
.pio_write(port.into(), &data[0..len])
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
}
} else {
if let Some(vmmops) = &self.vmmops {
vmmops
.pio_read(port.into(), &mut data[0..len])
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
}
let v = u32::from_le_bytes(data);
/* Preserve high bits in EAX but clear out high bits in RAX */
let mask = 0xffffffff >> (32 - len * 8);
let eax = (info.rax as u32 & !mask) | (v & mask);
ret_rax = eax as u64;
}
let insn_len = info.header.instruction_length() as u64;
/* Advance RIP and update RAX */
let arr_reg_name_value = [
(
hv_register_name::HV_X64_REGISTER_RIP,
info.header.rip + insn_len,
),
(hv_register_name::HV_X64_REGISTER_RAX, ret_rax),
];
set_registers_64!(self.fd, arr_reg_name_value)
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
Ok(cpu::VmExit::Ignore)
}
hv_message_type_HVMSG_UNMAPPED_GPA => {
let info = x.to_memory_info().unwrap();
let insn_len = info.instruction_byte_count as usize;
assert!(insn_len > 0 && insn_len <= 16);
let mut context = MshvEmulatorContext {
vcpu: self,
map: (info.guest_virtual_address, info.guest_physical_address),
};
// Create a new emulator.
let mut emul = Emulator::new(&mut context);
// Emulate the trapped instruction, and only the first one.
let new_state = emul
.emulate_first_insn(self.vp_index as usize, &info.instruction_bytes)
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
// Set CPU state back.
context
.set_cpu_state(self.vp_index as usize, new_state)
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
Ok(cpu::VmExit::Ignore)
}
hv_message_type_HVMSG_X64_CPUID_INTERCEPT => {
let info = x.to_cpuid_info().unwrap();
debug!("cpuid eax: {:x}", info.rax);
Ok(cpu::VmExit::Ignore)
}
hv_message_type_HVMSG_X64_MSR_INTERCEPT => {
let info = x.to_msr_info().unwrap();
if info.header.intercept_access_type == 0 {
debug!("msr read: {:x}", info.msr_number);
} else {
debug!("msr write: {:x}", info.msr_number);
}
Ok(cpu::VmExit::Ignore)
}
hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT => {
//TODO: Handler for VMCALL here.
let info = x.to_exception_info().unwrap();
debug!("Exception Info {:?}", info.exception_vector);
Ok(cpu::VmExit::Ignore)
}
exit => Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
"Unhandled VCPU exit {:?}",
exit
))),
},
Err(e) => match e.errno() {
libc::EAGAIN | libc::EINTR => Ok(cpu::VmExit::Ignore),
_ => Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
"VCPU error {:?}",
e
))),
},
}
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call to setup the CPUID registers.
///
fn set_cpuid2(&self, _cpuid: &CpuId) -> cpu::Result<()> {
Ok(())
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call to retrieve the CPUID registers.
///
fn get_cpuid2(&self, _num_entries: usize) -> cpu::Result<CpuId> {
Ok(self.cpuid.clone())
}
#[cfg(target_arch = "x86_64")]
///
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
///
fn get_lapic(&self) -> cpu::Result<LapicState> {
self.fd
.get_lapic()
.map_err(|e| cpu::HypervisorCpuError::GetlapicState(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
///
fn set_lapic(&self, lapic: &LapicState) -> cpu::Result<()> {
self.fd
.set_lapic(lapic)
.map_err(|e| cpu::HypervisorCpuError::SetLapicState(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that returns the vcpu's current "xsave struct".
///
fn get_xsave(&self) -> cpu::Result<Xsave> {
self.fd
.get_xsave()
.map_err(|e| cpu::HypervisorCpuError::GetXsaveState(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that sets the vcpu's current "xsave struct".
///
fn set_xsave(&self, xsave: &Xsave) -> cpu::Result<()> {
self.fd
.set_xsave(*xsave)
.map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into()))
}
///
/// Set CPU state
///
fn set_state(&self, state: &CpuState) -> cpu::Result<()> {
self.set_msrs(&state.msrs)?;
self.set_vcpu_events(&state.vcpu_events)?;
self.set_regs(&state.regs)?;
self.set_sregs(&state.sregs)?;
self.set_fpu(&state.fpu)?;
self.set_xcrs(&state.xcrs)?;
self.set_lapic(&state.lapic)?;
self.set_xsave(&state.xsave)?;
self.fd
.set_debug_regs(&state.dbg)
.map_err(|e| cpu::HypervisorCpuError::SetDebugRegs(e.into()))?;
Ok(())
}
///
/// Get CPU State
///
fn state(&self) -> cpu::Result<CpuState> {
let regs = self.get_regs()?;
let sregs = self.get_sregs()?;
let xcrs = self.get_xcrs()?;
let fpu = self.get_fpu()?;
let vcpu_events = self.get_vcpu_events()?;
let mut msrs = self.msrs.clone();
self.get_msrs(&mut msrs)?;
let lapic = self.get_lapic()?;
let xsave = self.get_xsave()?;
let dbg = self
.fd
.get_debug_regs()
.map_err(|e| cpu::HypervisorCpuError::GetDebugRegs(e.into()))?;
Ok(CpuState {
msrs,
vcpu_events,
regs,
sregs,
fpu,
xcrs,
lapic,
dbg,
xsave,
})
}
#[cfg(target_arch = "x86_64")]
///
/// Translate guest virtual address to guest physical address
///
fn translate_gva(&self, gva: u64, flags: u64) -> cpu::Result<(u64, hv_translate_gva_result)> {
let r = self
.fd
.translate_gva(gva, flags)
.map_err(|e| cpu::HypervisorCpuError::TranslateVirtualAddress(e.into()))?;
Ok(r)
}
}
struct MshvEmulatorContext<'a> {
vcpu: &'a MshvVcpu,
map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
}
impl<'a> MshvEmulatorContext<'a> {
// Do the actual gva -> gpa translation
#[allow(non_upper_case_globals)]
fn translate(&self, gva: u64) -> Result<u64, PlatformError> {
if self.map.0 == gva {
return Ok(self.map.1);
}
// TODO: More fine-grained control for the flags
let flags = HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_WRITE;
let r = self
.vcpu
.translate_gva(gva, flags.into())
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
let result_code = unsafe { r.1.__bindgen_anon_1.result_code };
match result_code {
hv_translate_gva_result_code_HvTranslateGvaSuccess => Ok(r.0),
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
}
}
}
/// Platform emulation for Hyper-V
impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
type CpuState = EmulatorCpuState;
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
let gpa = self.translate(gva)?;
debug!(
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
data.len(),
gva,
gpa
);
if let Some(vmmops) = &self.vcpu.vmmops {
if vmmops.guest_mem_read(gpa, data).is_err() {
vmmops
.mmio_read(gpa, data)
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
}
}
Ok(())
}
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
let gpa = self.translate(gva)?;
debug!(
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
data.len(),
gva,
gpa
);
if let Some(vmmops) = &self.vcpu.vmmops {
if vmmops.guest_mem_write(gpa, data).is_err() {
vmmops
.mmio_write(gpa, data)
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
}
}
Ok(())
}
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
if cpu_id != self.vcpu.vp_index as usize {
return Err(PlatformError::GetCpuStateFailure(anyhow!(
"CPU id mismatch {:?} {:?}",
cpu_id,
self.vcpu.vp_index
)));
}
let regs = self
.vcpu
.get_regs()
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
let sregs = self
.vcpu
.get_sregs()
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
debug!("mshv emulator: Getting new CPU state");
debug!("mshv emulator: {:#x?}", regs);
Ok(EmulatorCpuState { regs, sregs })
}
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError> {
if cpu_id != self.vcpu.vp_index as usize {
return Err(PlatformError::SetCpuStateFailure(anyhow!(
"CPU id mismatch {:?} {:?}",
cpu_id,
self.vcpu.vp_index
)));
}
debug!("mshv emulator: Setting new CPU state");
debug!("mshv emulator: {:#x?}", state.regs);
self.vcpu
.set_regs(&state.regs)
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
self.vcpu
.set_sregs(&state.sregs)
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))
}
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
self.translate(gva)
}
fn fetch(&self, _ip: u64, _instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
Err(PlatformError::MemoryReadFailure(anyhow!("unimplemented")))
}
}
#[allow(dead_code)]
/// Wrapper over Mshv VM ioctls.
pub struct MshvVm {
fd: Arc<VmFd>,
msrs: MsrEntries,
// Hypervisor State
hv_state: Arc<RwLock<HvState>>,
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
}
fn hv_state_init() -> Arc<RwLock<HvState>> {
Arc::new(RwLock::new(HvState { hypercall_page: 0 }))
}
///
/// Implementation of Vm trait for Mshv
/// Example:
/// #[cfg(feature = "mshv")]
/// # extern crate hypervisor;
/// # use hypervisor::MshvHypervisor;
/// let mshv = MshvHypervisor::new().unwrap();
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(mshv);
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
/// vm.set/get().unwrap()
///
impl vm::Vm for MshvVm {
#[cfg(target_arch = "x86_64")]
///
/// Sets the address of the three-page region in the VM's address space.
///
fn set_tss_address(&self, _offset: usize) -> vm::Result<()> {
Ok(())
}
///
/// Creates an in-kernel interrupt controller.
///
fn create_irq_chip(&self) -> vm::Result<()> {
Ok(())
}
///
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
///
fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
debug!("register_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
self.fd
.register_irqfd(&fd, gsi)
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
Ok(())
}
///
/// Unregisters an event that will, when signaled, trigger the `gsi` IRQ.
///
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
debug!("unregister_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
self.fd
.unregister_irqfd(&fd, gsi)
.map_err(|e| vm::HypervisorVmError::UnregisterIrqFd(e.into()))?;
Ok(())
}
///
/// Creates a VcpuFd object from a vcpu RawFd.
///
fn create_vcpu(
&self,
id: u8,
vmmops: Option<Arc<Box<dyn VmmOps>>>,
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
let vcpu_fd = self
.fd
.create_vcpu(id)
.map_err(|e| vm::HypervisorVmError::CreateVcpu(e.into()))?;
let vcpu = MshvVcpu {
fd: vcpu_fd,
vp_index: id,
cpuid: CpuId::new(1).unwrap(),
msrs: self.msrs.clone(),
hv_state: self.hv_state.clone(),
vmmops,
};
Ok(Arc::new(vcpu))
}
#[cfg(target_arch = "x86_64")]
fn enable_split_irq(&self) -> vm::Result<()> {
Ok(())
}
fn register_ioevent(
&self,
fd: &EventFd,
addr: &IoEventAddress,
datamatch: Option<DataMatch>,
) -> vm::Result<()> {
debug!(
"register_ioevent fd {} addr {:x?} datamatch {:?}",
fd.as_raw_fd(),
addr,
datamatch
);
if let Some(dm) = datamatch {
match dm {
vm::DataMatch::DataMatch32(mshv_dm32) => self
.fd
.register_ioevent(fd, addr, mshv_dm32)
.map_err(|e| vm::HypervisorVmError::RegisterIoEvent(e.into())),
vm::DataMatch::DataMatch64(mshv_dm64) => self
.fd
.register_ioevent(fd, addr, mshv_dm64)
.map_err(|e| vm::HypervisorVmError::RegisterIoEvent(e.into())),
}
} else {
self.fd
.register_ioevent(fd, addr, NoDatamatch)
.map_err(|e| vm::HypervisorVmError::RegisterIoEvent(e.into()))
}
}
/// Unregister an event from a certain address it has been previously registered to.
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> vm::Result<()> {
debug!("unregister_ioevent fd {} addr {:x?}", fd.as_raw_fd(), addr);
self.fd
.unregister_ioevent(fd, addr, NoDatamatch)
.map_err(|e| vm::HypervisorVmError::UnregisterIoEvent(e.into()))
}
/// Creates/modifies a guest physical memory slot.
fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
self.fd
.map_user_memory(user_memory_region)
.map_err(|e| vm::HypervisorVmError::SetUserMemory(e.into()))?;
Ok(())
}
fn make_user_memory_region(
&self,
_slot: u32,
guest_phys_addr: u64,
memory_size: u64,
userspace_addr: u64,
readonly: bool,
_log_dirty_pages: bool,
) -> MemoryRegion {
let mut flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_EXECUTABLE;
if !readonly {
flags |= HV_MAP_GPA_WRITABLE;
}
mshv_user_mem_region {
flags,
guest_pfn: guest_phys_addr >> PAGE_SHIFT,
size: memory_size,
userspace_addr: userspace_addr as u64,
}
}
fn create_passthrough_device(&self) -> vm::Result<Arc<dyn device::Device>> {
Err(vm::HypervisorVmError::CreatePassthroughDevice(anyhow!(
"No passthrough support"
)))
}
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
let mut msi_routing =
vec_with_array_field::<mshv_msi_routing, mshv_msi_routing_entry>(entries.len());
msi_routing[0].nr = entries.len() as u32;
unsafe {
let entries_slice: &mut [mshv_msi_routing_entry] =
msi_routing[0].entries.as_mut_slice(entries.len());
entries_slice.copy_from_slice(&entries);
}
self.fd
.set_msi_routing(&msi_routing[0])
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
}
///
/// Get the Vm state. Return VM specific data
///
fn state(&self) -> vm::Result<VmState> {
Ok(*self.hv_state.read().unwrap())
}
///
/// Set the VM state
///
fn set_state(&self, state: VmState) -> vm::Result<()> {
self.hv_state.write().unwrap().hypercall_page = state.hypercall_page;
Ok(())
}
///
/// Get dirty pages bitmap (one bit per page)
///
fn get_dirty_log(&self, _slot: u32, _memory_size: u64) -> vm::Result<Vec<u64>> {
Err(vm::HypervisorVmError::GetDirtyLog(anyhow!(
"get_dirty_log not implemented"
)))
}
}
pub use hv_cpuid_entry as CpuIdEntry;
pub type IrqRoutingEntry = mshv_msi_routing_entry;
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;

View File

@@ -0,0 +1,123 @@
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
// Copyright 2018-2019 CrowdStrike, Inc.
//
//
use crate::arch::x86::{msr_index, SegmentRegisterOps};
use serde_derive::{Deserialize, Serialize};
///
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
///
pub use {
mshv_bindings::mshv_user_mem_region as MemoryRegion, mshv_bindings::msr_entry as MsrEntry,
mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
mshv_bindings::FloatingPointUnit as FpuState, mshv_bindings::LapicState,
mshv_bindings::MsrList, mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs,
mshv_bindings::SegmentRegister, mshv_bindings::SpecialRegisters,
mshv_bindings::StandardRegisters, mshv_bindings::VcpuEvents, mshv_bindings::XSave as Xsave,
mshv_bindings::Xcrs as ExtendedControlRegisters,
};
#[derive(Clone, Serialize, Deserialize)]
pub struct VcpuMshvState {
pub msrs: MsrEntries,
pub vcpu_events: VcpuEvents,
pub regs: StandardRegisters,
pub sregs: SpecialRegisters,
pub fpu: FpuState,
pub xcrs: ExtendedControlRegisters,
pub lapic: LapicState,
pub dbg: DebugRegisters,
pub xsave: Xsave,
}
pub struct CreateDevice {}
pub struct DeviceAttr {}
pub struct IrqRouting {}
pub enum VcpuExit {}
pub struct MpState {}
impl SegmentRegisterOps for SegmentRegister {
fn segment_type(&self) -> u8 {
self.type_
}
fn set_segment_type(&mut self, val: u8) {
self.type_ = val;
}
fn dpl(&self) -> u8 {
self.dpl
}
fn set_dpl(&mut self, val: u8) {
self.dpl = val;
}
fn present(&self) -> u8 {
self.present
}
fn set_present(&mut self, val: u8) {
self.present = val;
}
fn long(&self) -> u8 {
self.l
}
fn set_long(&mut self, val: u8) {
self.l = val;
}
fn avl(&self) -> u8 {
self.avl
}
fn set_avl(&mut self, val: u8) {
self.avl = val;
}
fn desc_type(&self) -> u8 {
self.s
}
fn set_desc_type(&mut self, val: u8) {
self.s = val;
}
fn granularity(&self) -> u8 {
self.g
}
fn set_granularity(&mut self, val: u8) {
self.g = val;
}
fn db(&self) -> u8 {
self.db
}
fn set_db(&mut self, val: u8) {
self.db = val;
}
}
pub fn boot_msr_entries() -> MsrEntries {
MsrEntries::from_entries(&[
msr!(msr_index::MSR_IA32_SYSENTER_CS),
msr!(msr_index::MSR_IA32_SYSENTER_ESP),
msr!(msr_index::MSR_IA32_SYSENTER_EIP),
msr!(msr_index::MSR_STAR),
msr!(msr_index::MSR_CSTAR),
msr!(msr_index::MSR_LSTAR),
msr!(msr_index::MSR_KERNEL_GS_BASE),
msr!(msr_index::MSR_SYSCALL_MASK),
msr!(msr_index::MSR_IA32_TSC),
])
.unwrap()
}

View File

@@ -12,10 +12,18 @@
use crate::aarch64::VcpuInit;
use crate::cpu::Vcpu;
use crate::device::Device;
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "tdx")]
use crate::x86_64::CpuId;
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
use crate::ClockData;
#[cfg(feature = "kvm")]
use crate::CreateDevice;
#[cfg(feature = "mshv")]
use crate::HvState as VmState;
#[cfg(feature = "kvm")]
use crate::KvmVmState as VmState;
use crate::{CreateDevice, IoEventAddress, IrqRoutingEntry, MemoryRegion};
use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion};
#[cfg(feature = "kvm")]
use kvm_ioctls::Cap;
use std::sync::Arc;
use thiserror::Error;
@@ -24,14 +32,15 @@ use vmm_sys_util::eventfd::EventFd;
///
/// I/O events data matches (32 or 64 bits).
///
#[derive(Debug)]
pub enum DataMatch {
DataMatch32(u32),
DataMatch64(u64),
}
impl Into<u64> for DataMatch {
fn into(self) -> u64 {
match self {
impl From<DataMatch> for u64 {
fn from(dm: DataMatch) -> u64 {
match dm {
DataMatch::DataMatch32(dm) => dm.into(),
DataMatch::DataMatch64(dm) => dm,
}
@@ -117,7 +126,6 @@ pub enum HypervisorVmError {
///
#[error("Failed to create passthrough device: {0}")]
CreatePassthroughDevice(#[source] anyhow::Error),
///
/// Write to Guest memory
///
#[error("Failed to write to guest memory: {0}")]
@@ -147,6 +155,35 @@ pub enum HypervisorVmError {
///
#[error("Failed to write to IO Bus: {0}")]
IoBusWrite(#[source] anyhow::Error),
///
/// Get dirty log error
///
#[error("Failed to get dirty log: {0}")]
GetDirtyLog(#[source] anyhow::Error),
///
/// Assert virtual interrupt error
///
#[error("Failed to assert virtual Interrupt: {0}")]
AsserttVirtualInterrupt(#[source] anyhow::Error),
#[cfg(feature = "tdx")]
///
/// Error initializing TDX on the VM
///
#[error("Failed to initialize TDX: {0}")]
InitializeTdx(#[source] std::io::Error),
#[cfg(feature = "tdx")]
///
/// Error finalizing the TDX configuration on the VM
///
#[error("Failed to finalize TDX: {0}")]
FinalizeTdx(#[source] std::io::Error),
#[cfg(feature = "tdx")]
///
/// Error initializing the TDX memory region
///
#[error("Failed to initialize memory region TDX: {0}")]
InitMemRegionTdx(#[source] std::io::Error),
}
///
/// Result type for returning from a function
@@ -169,7 +206,7 @@ pub trait Vm: Send + Sync {
/// Unregister an event that will, when signaled, trigger the `gsi` IRQ.
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
/// Creates a new KVM vCPU file descriptor and maps the memory corresponding
fn create_vcpu(&self, id: u8) -> Result<Arc<dyn Vcpu>>;
fn create_vcpu(&self, id: u8, vmmops: Option<Arc<Box<dyn VmmOps>>>) -> Result<Arc<dyn Vcpu>>;
/// Registers an event to be signaled whenever a certain address is written to.
fn register_ioevent(
&self,
@@ -189,9 +226,11 @@ pub trait Vm: Send + Sync {
memory_size: u64,
userspace_addr: u64,
readonly: bool,
log_dirty_pages: bool,
) -> MemoryRegion;
/// Creates/modifies a guest physical memory slot.
fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
#[cfg(feature = "kvm")]
/// Creates an emulated device in the kernel.
fn create_device(&self, device: &mut CreateDevice) -> Result<Arc<dyn Device>>;
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
@@ -201,11 +240,12 @@ pub trait Vm: Send + Sync {
#[cfg(target_arch = "x86_64")]
fn enable_split_irq(&self) -> Result<()>;
/// Retrieve guest clock.
#[cfg(target_arch = "x86_64")]
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
fn get_clock(&self) -> Result<ClockData>;
/// Set guest clock.
#[cfg(target_arch = "x86_64")]
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
fn set_clock(&self, data: &ClockData) -> Result<()>;
#[cfg(feature = "kvm")]
/// Checks if a particular `Cap` is available.
fn check_extension(&self, c: Cap) -> bool;
/// Create a device that is used for passthrough
@@ -214,17 +254,32 @@ pub trait Vm: Send + Sync {
fn state(&self) -> Result<VmState>;
/// Set the VM state
fn set_state(&self, state: VmState) -> Result<()>;
/// Set VmmOps interface
fn set_vmmops(&self, vmmops: Box<dyn VmmOps>) -> Result<()>;
/// Get dirty pages bitmap
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> Result<Vec<u64>>;
#[cfg(feature = "tdx")]
/// Initalize TDX on this VM
fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> Result<()>;
#[cfg(feature = "tdx")]
/// Finalize the configuration of TDX on this VM
fn tdx_finalize(&self) -> Result<()>;
#[cfg(feature = "tdx")]
/// Initalize a TDX memory region for this VM
fn tdx_init_memory_region(
&self,
host_address: u64,
guest_address: u64,
size: u64,
measure: bool,
) -> Result<()>;
}
pub trait VmmOps: Send + Sync {
fn guest_mem_write(&self, buf: &[u8], gpa: u64) -> Result<usize>;
fn guest_mem_read(&self, buf: &mut [u8], gpa: u64) -> Result<usize>;
fn mmio_read(&self, addr: u64, data: &mut [u8]) -> Result<()>;
fn mmio_write(&self, addr: u64, data: &[u8]) -> Result<()>;
fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>;
fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>;
fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>;
fn mmio_write(&self, gpa: u64, data: &[u8]) -> Result<()>;
#[cfg(target_arch = "x86_64")]
fn pio_read(&self, addr: u64, data: &mut [u8]) -> Result<()>;
fn pio_read(&self, port: u64, data: &mut [u8]) -> Result<()>;
#[cfg(target_arch = "x86_64")]
fn pio_write(&self, addr: u64, data: &[u8]) -> Result<()>;
fn pio_write(&self, port: u64, data: &[u8]) -> Result<()>;
}

View File

@@ -5,17 +5,16 @@ authors = ["The Chromium OS Authors"]
[dependencies]
epoll = ">=4.0.1"
libc = "0.2.80"
log = "0.4.11"
libc = "0.2.91"
log = "0.4.14"
net_gen = { path = "../net_gen" }
rand = "0.7.3"
serde = "1.0.117"
serde = "1.0.125"
virtio-bindings = "0.1.0"
vm-memory = { version = "0.3.0", features = ["backend-mmap", "backend-atomic"] }
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = ">=0.3.1"
[dev-dependencies]
lazy_static = "1.3.0"
pnet = "0.26.0"
serde_json = "1.0.59"
pnet = "0.27.2"
serde_json = "1.0.64"

View File

@@ -14,7 +14,6 @@ extern crate libc;
#[macro_use]
extern crate log;
extern crate net_gen;
extern crate rand;
extern crate serde;
extern crate virtio_bindings;
extern crate vm_memory;

View File

@@ -5,7 +5,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.
use rand::Rng;
use std::fmt;
use std::io;
use std::result::Result;
@@ -58,16 +57,19 @@ impl MacAddr {
// TODO: using something like std::mem::uninitialized could avoid the extra initialization,
// if this ever becomes a performance bottleneck.
let mut bytes = [0u8; MAC_ADDR_LEN];
bytes[..].copy_from_slice(&src[..]);
bytes[..].copy_from_slice(&src);
MacAddr { bytes }
}
// An error can only occur if the slice length is different from MAC_ADDR_LEN.
#[inline]
pub fn from_bytes(src: &[u8]) -> Result<MacAddr, ()> {
pub fn from_bytes(src: &[u8]) -> Result<MacAddr, io::Error> {
if src.len() != MAC_ADDR_LEN {
return Err(());
return Err(io::Error::new(
io::ErrorKind::Other,
format!("invalid length of slice: {} vs {}", src.len(), MAC_ADDR_LEN),
));
}
Ok(MacAddr::from_bytes_unchecked(src))
}
@@ -79,7 +81,22 @@ impl MacAddr {
pub fn local_random() -> MacAddr {
// Generate a fully random MAC
let mut random_bytes = rand::thread_rng().gen::<[u8; MAC_ADDR_LEN]>();
let mut random_bytes = [0u8; MAC_ADDR_LEN];
unsafe {
// Man page says this function will not be interrupted by a signal
// for requests less than 256 bytes
if libc::getrandom(
random_bytes.as_mut_ptr() as *mut _ as *mut libc::c_void,
MAC_ADDR_LEN,
0,
) < 0
{
error!(
"Error populating MAC address with random data: {}",
std::io::Error::last_os_error()
)
}
};
// Set the first byte to make the OUI a locally administered OUI
random_bytes[0] = 0x2e;

View File

@@ -65,6 +65,7 @@ pub fn open_tap(
netmask: Option<Ipv4Addr>,
host_mac: &mut Option<MacAddr>,
num_rx_q: usize,
flags: Option<i32>,
) -> Result<Vec<Tap>> {
let mut taps: Vec<Tap> = Vec::new();
let mut ifname: String = String::new();
@@ -82,7 +83,7 @@ pub fn open_tap(
let tap: Tap;
if i == 0 {
tap = match if_name {
Some(name) => Tap::open_named(name, num_rx_q).map_err(Error::TapOpen)?,
Some(name) => Tap::open_named(name, num_rx_q, flags).map_err(Error::TapOpen)?,
None => Tap::new(num_rx_q).map_err(Error::TapOpen)?,
};
if let Some(ip) = ip_addr {
@@ -104,7 +105,7 @@ pub fn open_tap(
ifname = String::from_utf8(tap.get_if_name()).unwrap();
} else {
tap = Tap::open_named(ifname.as_str(), num_rx_q).map_err(Error::TapOpen)?;
tap = Tap::open_named(ifname.as_str(), num_rx_q, flags).map_err(Error::TapOpen)?;
tap.set_offload(flag).map_err(Error::TapSetOffload)?;
tap.set_vnet_hdr_size(vnet_hdr_size)

View File

@@ -2,27 +2,17 @@
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use super::{register_listener, unregister_listener, vnet_hdr_len, Tap};
use libc::EAGAIN;
use std::cmp;
use super::{unregister_listener, vnet_hdr_len, Tap};
use std::io;
use std::io::{Read, Write};
use std::num::Wrapping;
use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap};
use vm_virtio::{DescriptorChain, Queue};
/// The maximum buffer size when segmentation offload is enabled. This
/// includes the 12-byte virtio net header.
/// http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html#x1-1740003
const MAX_BUFFER_SIZE: usize = 65562;
use vm_memory::{Bytes, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, GuestMemoryMmap};
use vm_virtio::Queue;
#[derive(Clone)]
pub struct TxVirtio {
pub iovec: Vec<(GuestAddress, usize)>,
pub frame_buf: [u8; MAX_BUFFER_SIZE],
pub counter_bytes: Wrapping<u64>,
pub counter_frames: Wrapping<u64>,
}
@@ -36,73 +26,66 @@ impl Default for TxVirtio {
impl TxVirtio {
pub fn new() -> Self {
TxVirtio {
iovec: Vec::new(),
frame_buf: [0u8; MAX_BUFFER_SIZE],
counter_bytes: Wrapping(0),
counter_frames: Wrapping(0),
}
}
pub fn process_desc_chain(&mut self, mem: &GuestMemoryMmap, tap: &mut Tap, queue: &mut Queue) {
pub fn process_desc_chain(
&mut self,
mem: &GuestMemoryMmap,
tap: &mut Tap,
queue: &mut Queue,
) -> Result<(), NetQueuePairError> {
while let Some(avail_desc) = queue.iter(&mem).next() {
let head_index = avail_desc.index;
let mut read_count = 0;
let mut next_desc = Some(avail_desc);
self.iovec.clear();
let mut iovecs = Vec::new();
while let Some(desc) = next_desc {
if desc.is_write_only() {
break;
if !desc.is_write_only() && desc.len > 0 {
let buf = mem
.get_slice(desc.addr, desc.len as usize)
.map_err(NetQueuePairError::GuestMemory)?
.as_ptr();
let iovec = libc::iovec {
iov_base: buf as *mut libc::c_void,
iov_len: desc.len as libc::size_t,
};
iovecs.push(iovec);
}
self.iovec.push((desc.addr, desc.len as usize));
read_count += desc.len as usize;
next_desc = desc.next_descriptor();
}
read_count = 0;
// Copy buffer from across multiple descriptors.
// TODO(performance - Issue #420): change this to use `writev()` instead of `write()`
// and get rid of the intermediate buffer.
for (desc_addr, desc_len) in self.iovec.drain(..) {
let limit = cmp::min((read_count + desc_len) as usize, self.frame_buf.len());
let read_result =
mem.read_slice(&mut self.frame_buf[read_count..limit as usize], desc_addr);
match read_result {
Ok(_) => {
// Increment by number of bytes actually read
read_count += limit - read_count;
}
Err(e) => {
println!("Failed to read slice: {:?}", e);
break;
}
if !iovecs.is_empty() {
let result = unsafe {
libc::writev(
tap.as_raw_fd() as libc::c_int,
iovecs.as_ptr() as *const libc::iovec,
iovecs.len() as libc::c_int,
)
};
if result < 0 {
let e = std::io::Error::last_os_error();
error!("net: tx: failed writing to tap: {}", e);
queue.go_to_previous_position();
return Err(NetQueuePairError::WriteTap(e));
}
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
self.counter_frames += Wrapping(1);
}
let write_result = tap.write(&self.frame_buf[..read_count]);
match write_result {
Ok(_) => {}
Err(e) => {
println!("net: tx: error failed to write to tap: {}", e);
}
};
self.counter_bytes += Wrapping((read_count - vnet_hdr_len()) as u64);
self.counter_frames += Wrapping(1);
queue.add_used(&mem, head_index, 0);
queue.update_avail_event(&mem);
}
Ok(())
}
}
#[derive(Clone)]
pub struct RxVirtio {
pub deferred_frame: bool,
pub deferred_irqs: bool,
pub bytes_read: usize,
pub frame_buf: [u8; MAX_BUFFER_SIZE],
pub counter_bytes: Wrapping<u64>,
pub counter_frames: Wrapping<u64>,
}
@@ -116,10 +99,6 @@ impl Default for RxVirtio {
impl RxVirtio {
pub fn new() -> Self {
RxVirtio {
deferred_frame: false,
deferred_irqs: false,
bytes_read: 0,
frame_buf: [0u8; MAX_BUFFER_SIZE],
counter_bytes: Wrapping(0),
counter_frames: Wrapping(0),
}
@@ -128,63 +107,72 @@ impl RxVirtio {
pub fn process_desc_chain(
&mut self,
mem: &GuestMemoryMmap,
mut next_desc: Option<DescriptorChain>,
tap: &mut Tap,
queue: &mut Queue,
) -> bool {
let head_index = next_desc.as_ref().unwrap().index;
let mut write_count = 0;
) -> Result<bool, NetQueuePairError> {
let mut exhausted_descs = true;
while let Some(avail_desc) = queue.iter(&mem).next() {
let head_index = avail_desc.index;
let num_buffers_addr = mem.checked_offset(avail_desc.addr, 10).unwrap();
let mut next_desc = Some(avail_desc);
// Copy from frame into buffer, which may span multiple descriptors.
loop {
match next_desc {
Some(desc) => {
if !desc.is_write_only() {
break;
}
let limit = cmp::min(write_count + desc.len as usize, self.bytes_read);
let source_slice = &self.frame_buf[write_count..limit];
let write_result = mem.write_slice(source_slice, desc.addr);
let mut iovecs = Vec::new();
while let Some(desc) = next_desc {
if desc.is_write_only() && desc.len > 0 {
let buf = mem
.get_slice(desc.addr, desc.len as usize)
.map_err(NetQueuePairError::GuestMemory)?
.as_ptr();
let iovec = libc::iovec {
iov_base: buf as *mut libc::c_void,
iov_len: desc.len as libc::size_t,
};
iovecs.push(iovec);
}
next_desc = desc.next_descriptor();
}
match write_result {
Ok(_) => {
write_count = limit;
}
Err(e) => {
error!("Failed to write slice: {:?}", e);
let len = if !iovecs.is_empty() {
let result = unsafe {
libc::readv(
tap.as_raw_fd() as libc::c_int,
iovecs.as_ptr() as *const libc::iovec,
iovecs.len() as libc::c_int,
)
};
if result < 0 {
let e = std::io::Error::last_os_error();
exhausted_descs = false;
queue.go_to_previous_position();
if let Some(raw_err) = e.raw_os_error() {
if raw_err == libc::EAGAIN {
break;
}
};
if write_count >= self.bytes_read {
break;
}
next_desc = desc.next_descriptor();
error!("net: rx: failed reading from tap: {}", e);
return Err(NetQueuePairError::ReadTap(e));
}
None => {
warn!("Receiving buffer is too small to hold frame of current size");
break;
}
}
// Write num_buffers to guest memory. We simply write 1 as we
// never spread the frame over more than one descriptor chain.
mem.write_obj(1u16, num_buffers_addr)
.map_err(NetQueuePairError::GuestMemory)?;
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
self.counter_frames += Wrapping(1);
result as u32
} else {
0
};
queue.add_used(&mem, head_index, len);
queue.update_avail_event(&mem);
}
self.counter_bytes += Wrapping((write_count - vnet_hdr_len()) as u64);
self.counter_frames += Wrapping(1);
queue.add_used(&mem, head_index, write_count as u32);
queue.update_avail_event(&mem);
// Mark that we have at least one pending packet and we need to interrupt the guest.
self.deferred_irqs = true;
// Update the frame_buf buffer.
if write_count < self.bytes_read {
self.frame_buf.copy_within(write_count..self.bytes_read, 0);
self.bytes_read -= write_count;
false
} else {
self.bytes_read = 0;
true
}
Ok(exhausted_descs)
}
}
@@ -204,8 +192,12 @@ pub enum NetQueuePairError {
RegisterListener(io::Error),
/// Error unregistering listener
UnregisterListener(io::Error),
/// Error writing to the TAP device
WriteTap(io::Error),
/// Error reading from the TAP device
FailedReadTap,
ReadTap(io::Error),
/// Error related to guest memory
GuestMemory(vm_memory::GuestMemoryError),
}
pub struct NetQueuePair {
@@ -220,128 +212,15 @@ pub struct NetQueuePair {
}
impl NetQueuePair {
// Copies a single frame from `self.rx.frame_buf` into the guest. Returns true
// if a buffer was used, and false if the frame must be deferred until a buffer
// is made available by the driver.
fn rx_single_frame(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
let mem = self
.mem
.as_ref()
.ok_or(NetQueuePairError::NoMemoryConfigured)
.map(|m| m.memory())?;
let next_desc = queue.iter(&mem).next();
if next_desc.is_none() {
// Queue has no available descriptors
if self.rx_tap_listening {
unregister_listener(
self.epoll_fd.unwrap(),
self.tap.as_raw_fd(),
epoll::Events::EPOLLIN,
u64::from(self.tap_event_id),
)
.map_err(NetQueuePairError::UnregisterListener)?;
self.rx_tap_listening = false;
info!("Listener unregistered");
}
return Ok(false);
}
Ok(self.rx.process_desc_chain(&mem, next_desc, &mut queue))
}
fn process_rx(&mut self, queue: &mut Queue) -> Result<bool, NetQueuePairError> {
// Read as many frames as possible.
loop {
match self.read_tap() {
Ok(count) => {
self.rx.bytes_read = count;
if !self.rx_single_frame(queue)? {
self.rx.deferred_frame = true;
break;
}
}
Err(e) => {
// The tap device is non-blocking, so any error aside from EAGAIN is
// unexpected.
match e.raw_os_error() {
Some(err) if err == EAGAIN => (),
_ => {
error!("Failed to read tap: {:?}", e);
return Err(NetQueuePairError::FailedReadTap);
}
};
break;
}
}
}
// Consume the counters from the Rx/Tx queues and accumulate into
// the counters for the device as whole. This consumption is needed
// to handle MQ.
self.counters
.rx_bytes
.fetch_add(self.rx.counter_bytes.0, Ordering::AcqRel);
self.counters
.rx_frames
.fetch_add(self.rx.counter_frames.0, Ordering::AcqRel);
self.rx.counter_bytes = Wrapping(0);
self.rx.counter_frames = Wrapping(0);
if self.rx.deferred_irqs {
self.rx.deferred_irqs = false;
let mem = self
.mem
.as_ref()
.ok_or(NetQueuePairError::NoMemoryConfigured)
.map(|m| m.memory())?;
Ok(queue.needs_notification(&mem, queue.next_used))
} else {
Ok(false)
}
}
pub fn resume_rx(&mut self, queue: &mut Queue) -> Result<bool, NetQueuePairError> {
if !self.rx_tap_listening {
register_listener(
self.epoll_fd.unwrap(),
self.tap.as_raw_fd(),
epoll::Events::EPOLLIN,
u64::from(self.tap_event_id),
)
.map_err(NetQueuePairError::RegisterListener)?;
self.rx_tap_listening = true;
info!("Listener registered");
}
if self.rx.deferred_frame {
if self.rx_single_frame(queue)? {
self.rx.deferred_frame = false;
// process_rx() was interrupted possibly before consuming all
// packets in the tap; try continuing now.
self.process_rx(queue)
} else if self.rx.deferred_irqs {
self.rx.deferred_irqs = false;
let mem = self
.mem
.as_ref()
.ok_or(NetQueuePairError::NoMemoryConfigured)
.map(|m| m.memory())?;
Ok(queue.needs_notification(&mem, queue.next_used))
} else {
Ok(false)
}
} else {
Ok(false)
}
}
pub fn process_tx(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
let mem = self
.mem
.as_ref()
.ok_or(NetQueuePairError::NoMemoryConfigured)
.map(|m| m.memory())?;
self.tx.process_desc_chain(&mem, &mut self.tap, &mut queue);
self.tx
.process_desc_chain(&mem, &mut self.tap, &mut queue)?;
self.counters
.tx_bytes
@@ -355,26 +234,37 @@ impl NetQueuePair {
Ok(queue.needs_notification(&mem, queue.next_used))
}
pub fn process_rx_tap(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
if self.rx.deferred_frame
// Process a deferred frame first if available. Don't read from tap again
// until we manage to receive this deferred frame.
{
if self.rx_single_frame(&mut queue)? {
self.rx.deferred_frame = false;
self.process_rx(&mut queue)
} else if self.rx.deferred_irqs {
self.rx.deferred_irqs = false;
Ok(true)
} else {
Ok(false)
}
} else {
self.process_rx(&mut queue)
}
}
pub fn process_rx(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
let mem = self
.mem
.as_ref()
.ok_or(NetQueuePairError::NoMemoryConfigured)
.map(|m| m.memory())?;
fn read_tap(&mut self) -> io::Result<usize> {
self.tap.read(&mut self.rx.frame_buf)
if self
.rx
.process_desc_chain(&mem, &mut self.tap, &mut queue)?
&& self.rx_tap_listening
{
unregister_listener(
self.epoll_fd.unwrap(),
self.tap.as_raw_fd(),
epoll::Events::EPOLLIN,
u64::from(self.tap_event_id),
)
.map_err(NetQueuePairError::UnregisterListener)?;
self.rx_tap_listening = false;
}
self.counters
.rx_bytes
.fetch_add(self.rx.counter_bytes.0, Ordering::AcqRel);
self.counters
.rx_frames
.fetch_add(self.rx.counter_frames.0, Ordering::AcqRel);
self.rx.counter_bytes = Wrapping(0);
self.rx.counter_frames = Wrapping(0);
Ok(queue.needs_notification(&mem, queue.next_used))
}
}

View File

@@ -5,7 +5,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.
use super::{create_sockaddr, create_socket, Error as NetUtilError, MacAddr};
use super::{create_sockaddr, create_socket, vnet_hdr_len, Error as NetUtilError, MacAddr};
use mac::MAC_ADDR_LEN;
use net_gen;
use std::fs::File;
@@ -31,7 +31,7 @@ pub enum Error {
NetUtil(NetUtilError),
InvalidIfname,
/// Error parsing MAC data
MacParsing(()),
MacParsing(IoError),
}
pub type Result<T> = ::std::result::Result<T, Error>;
@@ -83,7 +83,7 @@ fn build_terminated_if_name(if_name: &str) -> Result<Vec<u8>> {
}
impl Tap {
pub fn open_named(if_name: &str, num_queue_pairs: usize) -> Result<Tap> {
pub fn open_named(if_name: &str, num_queue_pairs: usize, flags: Option<i32>) -> Result<Tap> {
let terminated_if_name = build_terminated_if_name(if_name)?;
let fd = unsafe {
@@ -91,7 +91,7 @@ impl Tap {
// string and verify the result.
libc::open(
b"/dev/net/tun\0".as_ptr() as *const c_char,
libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC,
flags.unwrap_or(libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC),
)
};
if fd < 0 {
@@ -150,7 +150,54 @@ impl Tap {
/// Create a new tap interface.
pub fn new(num_queue_pairs: usize) -> Result<Tap> {
Self::open_named("vmtap%d", num_queue_pairs)
Self::open_named("vmtap%d", num_queue_pairs, None)
}
pub fn from_tap_fd(fd: RawFd, num_queue_pairs: usize) -> Result<Tap> {
// Ensure that the file is opened non-blocking, this is particularly
// needed when opened via the shell for macvtap.
let ret = unsafe {
let mut flags = libc::fcntl(fd, libc::F_GETFL);
flags |= libc::O_NONBLOCK;
libc::fcntl(fd, libc::F_SETFL, flags)
};
if ret < 0 {
return Err(Error::ConfigureTap(IoError::last_os_error()));
}
let tap_file = unsafe { File::from_raw_fd(fd) };
let mut ifreq: net_gen::ifreq = Default::default();
// Get current config including name
let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNGETIFF(), &mut ifreq) };
if ret < 0 {
return Err(Error::IoctlError(IoError::last_os_error()));
}
let if_name = unsafe { *ifreq.ifr_ifrn.ifrn_name.as_ref() }.to_vec();
// Try and update flags. Depending on how the tap was created (macvtap
// or via open_named()) this might return -EEXIST so we just ignore that.
unsafe {
let ifru_flags = ifreq.ifr_ifru.ifru_flags.as_mut();
*ifru_flags =
(net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as c_short;
if num_queue_pairs > 1 {
*ifru_flags |= net_gen::IFF_MULTI_QUEUE as c_short;
}
}
let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNSETIFF(), &mut ifreq) };
if ret < 0 && IoError::last_os_error().raw_os_error().unwrap() != libc::EEXIST {
return Err(Error::ConfigureTap(IoError::last_os_error()));
}
let tap = Tap { if_name, tap_file };
let offload_flags =
net_gen::TUN_F_CSUM | net_gen::TUN_F_UFO | net_gen::TUN_F_TSO4 | net_gen::TUN_F_TSO6;
let vnet_hdr_size = vnet_hdr_len() as i32;
tap.set_offload(offload_flags)?;
tap.set_vnet_hdr_size(vnet_hdr_size)?;
Ok(tap)
}
/// Set the host-side IP address for the tap interface.
@@ -535,10 +582,21 @@ mod tests {
#[test]
fn test_tap_create() {
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
let t = Tap::new(1).unwrap();
println!("created tap: {:?}", t);
}
#[test]
fn test_tap_from_fd() {
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
let orig_tap = Tap::new(1).unwrap();
let fd = orig_tap.as_raw_fd();
let _new_tap = Tap::from_tap_fd(fd, 1).unwrap();
}
#[test]
fn test_tap_configure() {
// This should be the first thing to be called inside the function, so everything else
@@ -559,6 +617,8 @@ mod tests {
#[test]
fn test_set_options() {
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
// This line will fail to provide an initialized FD if the test is not run as root.
let tap = Tap::new(1).unwrap();
tap.set_vnet_hdr_size(16).unwrap();
@@ -567,6 +627,8 @@ mod tests {
#[test]
fn test_tap_enable() {
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
let tap = Tap::new(1).unwrap();
let ret = tap.enable();
assert!(ret.is_ok());
@@ -574,6 +636,8 @@ mod tests {
#[test]
fn test_tap_get_ifreq() {
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
let tap = Tap::new(1).unwrap();
let ret = tap.get_ifreq();
assert_eq!(
@@ -584,6 +648,8 @@ mod tests {
#[test]
fn test_raw_fd() {
let _tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
let tap = Tap::new(1).unwrap();
assert_eq!(tap.as_raw_fd(), tap.tap_file.as_raw_fd());
}

View File

@@ -6,18 +6,18 @@ edition = "2018"
[dependencies]
anyhow = "1.0"
byteorder = "1.3.4"
byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" }
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio-ioctls", branch = "master" }
vmm-sys-util = ">=0.3.1"
libc = "0.2.80"
log = "0.4.11"
libc = "0.2.91"
log = "0.4.14"
serde = {version = ">=1.0.27", features = ["rc"] }
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }
vm-memory = "0.3.0"
vm-memory = "0.5.0"
vm-migration = { path = "../vm-migration" }
[dependencies.vfio-bindings]

View File

@@ -10,7 +10,7 @@ use byteorder::{ByteOrder, LittleEndian};
use std::any::Any;
use std::collections::HashMap;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Barrier, Mutex};
use vm_device::{Bus, BusDevice};
use vm_memory::{Address, GuestAddress, GuestUsize};
@@ -71,8 +71,14 @@ impl PciRoot {
impl BusDevice for PciRoot {}
impl PciDevice for PciRoot {
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
fn write_config_register(
&mut self,
reg_idx: usize,
offset: u64,
data: &[u8],
) -> Option<Arc<Barrier>> {
self.config.write_config_register(reg_idx, offset, data);
None
}
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
@@ -116,7 +122,7 @@ impl PciBus {
) -> Result<()> {
for (address, size, type_) in bars {
match type_ {
PciBarRegionType::IORegion => {
PciBarRegionType::IoRegion => {
#[cfg(target_arch = "x86_64")]
io_bus
.insert(dev.clone(), address.raw_value(), size)
@@ -225,14 +231,14 @@ impl PciConfigIo {
})
}
pub fn config_space_write(&mut self, offset: u64, data: &[u8]) {
pub fn config_space_write(&mut self, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if offset as usize + data.len() > 4 {
return;
return None;
}
let enabled = (self.config_address & 0x8000_0000) != 0;
if !enabled {
return;
return None;
}
let (bus, device, _function, register) =
@@ -240,7 +246,7 @@ impl PciConfigIo {
// Only support one bus.
if bus != 0 {
return;
return None;
}
let pci_bus = self.pci_bus.lock().unwrap();
@@ -265,7 +271,9 @@ impl PciConfigIo {
}
// Update the register value
device.write_config_register(register, offset, data);
device.write_config_register(register, offset, data)
} else {
None
}
}
@@ -312,13 +320,16 @@ impl BusDevice for PciConfigIo {
}
}
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
// `offset` is relative to 0xcf8
match offset {
o @ 0..=3 => self.set_config_address(o, data),
o @ 0..=3 => {
self.set_config_address(o, data);
None
}
o @ 4..=7 => self.config_space_write(o - 4, data),
_ => (),
};
_ => None,
}
}
}
@@ -407,11 +418,13 @@ impl BusDevice for PciConfigMmio {
}
}
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if offset > u64::from(u32::max_value()) {
return;
return None;
}
self.config_space_write(offset as u32, offset % 4, data)
self.config_space_write(offset as u32, offset % 4, data);
None
}
}

View File

@@ -100,7 +100,7 @@ pub enum PciBridgeSubclass {
PcmciaBridge = 0x05,
NuBusBridge = 0x06,
CardBusBridge = 0x07,
RACEwayBridge = 0x08,
RacEwayBridge = 0x08,
PciToPciSemiTransparentBridge = 0x09,
InfiniBrandToPciHostBridge = 0x0a,
OtherBridgeDevice = 0x80,
@@ -117,9 +117,9 @@ impl PciSubclass for PciBridgeSubclass {
#[derive(Copy, Clone)]
pub enum PciSerialBusSubClass {
Firewire = 0x00,
ACCESSbus = 0x01,
SSA = 0x02,
USB = 0x03,
Accessbus = 0x01,
Ssa = 0x02,
Usb = 0x03,
}
impl PciSubclass for PciSerialBusSubClass {
@@ -132,15 +132,15 @@ impl PciSubclass for PciSerialBusSubClass {
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum PciMassStorageSubclass {
SCSIStorage = 0x00,
IDEInterface = 0x01,
ScsiStorage = 0x00,
IdeInterface = 0x01,
FloppyController = 0x02,
IPIController = 0x03,
RAIDController = 0x04,
ATAController = 0x05,
SATAController = 0x06,
SerialSCSIController = 0x07,
NVMController = 0x08,
IpiController = 0x03,
RaidController = 0x04,
AtaController = 0x05,
SataController = 0x06,
SerialScsiController = 0x07,
NvmController = 0x08,
MassStorage = 0x80,
}
@@ -156,11 +156,11 @@ impl PciSubclass for PciMassStorageSubclass {
pub enum PciNetworkControllerSubclass {
EthernetController = 0x00,
TokenRingController = 0x01,
FDDIController = 0x02,
ATMController = 0x03,
ISDNController = 0x04,
FddiController = 0x02,
AtmController = 0x03,
IsdnController = 0x04,
WorldFipController = 0x05,
PICMGController = 0x06,
PicmgController = 0x06,
InfinibandController = 0x07,
FabricController = 0x08,
NetworkController = 0x80,
@@ -186,55 +186,55 @@ pub trait PciProgrammingInterface {
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[repr(C)]
pub enum PciCapabilityID {
ListID = 0,
pub enum PciCapabilityId {
ListId = 0,
PowerManagement = 0x01,
AcceleratedGraphicsPort = 0x02,
VitalProductData = 0x03,
SlotIdentification = 0x04,
MessageSignalledInterrupts = 0x05,
CompactPCIHotSwap = 0x06,
PCIX = 0x07,
CompactPciHotSwap = 0x06,
PciX = 0x07,
HyperTransport = 0x08,
VendorSpecific = 0x09,
Debugport = 0x0A,
CompactPCICentralResourceControl = 0x0B,
PCIStandardHotPlugController = 0x0C,
BridgeSubsystemVendorDeviceID = 0x0D,
AGPTargetPCIPCIbridge = 0x0E,
CompactPciCentralResourceControl = 0x0B,
PciStandardHotPlugController = 0x0C,
BridgeSubsystemVendorDeviceId = 0x0D,
AgpTargetPciPcibridge = 0x0E,
SecureDevice = 0x0F,
PCIExpress = 0x10,
MSIX = 0x11,
SATADataIndexConf = 0x12,
PCIAdvancedFeatures = 0x13,
PCIEnhancedAllocation = 0x14,
PciExpress = 0x10,
MsiX = 0x11,
SataDataIndexConf = 0x12,
PciAdvancedFeatures = 0x13,
PciEnhancedAllocation = 0x14,
}
impl From<u8> for PciCapabilityID {
impl From<u8> for PciCapabilityId {
fn from(c: u8) -> Self {
match c {
0 => PciCapabilityID::ListID,
0x01 => PciCapabilityID::PowerManagement,
0x02 => PciCapabilityID::AcceleratedGraphicsPort,
0x03 => PciCapabilityID::VitalProductData,
0x04 => PciCapabilityID::SlotIdentification,
0x05 => PciCapabilityID::MessageSignalledInterrupts,
0x06 => PciCapabilityID::CompactPCIHotSwap,
0x07 => PciCapabilityID::PCIX,
0x08 => PciCapabilityID::HyperTransport,
0x09 => PciCapabilityID::VendorSpecific,
0x0A => PciCapabilityID::Debugport,
0x0B => PciCapabilityID::CompactPCICentralResourceControl,
0x0C => PciCapabilityID::PCIStandardHotPlugController,
0x0D => PciCapabilityID::BridgeSubsystemVendorDeviceID,
0x0E => PciCapabilityID::AGPTargetPCIPCIbridge,
0x0F => PciCapabilityID::SecureDevice,
0x10 => PciCapabilityID::PCIExpress,
0x11 => PciCapabilityID::MSIX,
0x12 => PciCapabilityID::SATADataIndexConf,
0x13 => PciCapabilityID::PCIAdvancedFeatures,
0x14 => PciCapabilityID::PCIEnhancedAllocation,
_ => PciCapabilityID::ListID,
0 => PciCapabilityId::ListId,
0x01 => PciCapabilityId::PowerManagement,
0x02 => PciCapabilityId::AcceleratedGraphicsPort,
0x03 => PciCapabilityId::VitalProductData,
0x04 => PciCapabilityId::SlotIdentification,
0x05 => PciCapabilityId::MessageSignalledInterrupts,
0x06 => PciCapabilityId::CompactPciHotSwap,
0x07 => PciCapabilityId::PciX,
0x08 => PciCapabilityId::HyperTransport,
0x09 => PciCapabilityId::VendorSpecific,
0x0A => PciCapabilityId::Debugport,
0x0B => PciCapabilityId::CompactPciCentralResourceControl,
0x0C => PciCapabilityId::PciStandardHotPlugController,
0x0D => PciCapabilityId::BridgeSubsystemVendorDeviceId,
0x0E => PciCapabilityId::AgpTargetPciPcibridge,
0x0F => PciCapabilityId::SecureDevice,
0x10 => PciCapabilityId::PciExpress,
0x11 => PciCapabilityId::MsiX,
0x12 => PciCapabilityId::SataDataIndexConf,
0x13 => PciCapabilityId::PciAdvancedFeatures,
0x14 => PciCapabilityId::PciEnhancedAllocation,
_ => PciCapabilityId::ListId,
}
}
}
@@ -242,7 +242,7 @@ impl From<u8> for PciCapabilityID {
/// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
pub trait PciCapability {
fn bytes(&self) -> &[u8];
fn id(&self) -> PciCapabilityID;
fn id(&self) -> PciCapabilityId;
}
fn encode_32_bits_bar_size(bar_size: u32) -> Option<u32> {
@@ -317,7 +317,7 @@ pub struct PciConfiguration {
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum PciBarRegionType {
Memory32BitRegion = 0,
IORegion = 0x01,
IoRegion = 0x01,
Memory64BitRegion = 0x04,
}
@@ -481,7 +481,7 @@ impl PciConfiguration {
pub fn write_reg(&mut self, reg_idx: usize, value: u32) {
let mut mask = self.writable_bits[reg_idx];
if reg_idx >= BAR0_REG && reg_idx < BAR0_REG + NUM_BAR_REGS {
if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) {
// Handle very specific case where the BAR is being written with
// all 1's to retrieve the BAR size during next BAR reading.
if value == 0xffff_ffff {
@@ -570,9 +570,9 @@ impl PciConfiguration {
let end_addr = config
.addr
.checked_add(config.size - 1)
.ok_or_else(|| Error::BarAddressInvalid(config.addr, config.size))?;
.ok_or(Error::BarAddressInvalid(config.addr, config.size))?;
match config.region_type {
PciBarRegionType::Memory32BitRegion | PciBarRegionType::IORegion => {
PciBarRegionType::Memory32BitRegion | PciBarRegionType::IoRegion => {
if end_addr > u64::from(u32::max_value()) {
return Err(Error::BarAddressInvalid(config.addr, config.size));
}
@@ -614,7 +614,7 @@ impl PciConfiguration {
BAR_MEM_ADDR_MASK,
config.prefetchable as u32 | config.region_type as u32,
),
PciBarRegionType::IORegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
PciBarRegionType::IoRegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
};
self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
@@ -642,7 +642,7 @@ impl PciConfiguration {
let end_addr = config
.addr
.checked_add(config.size - 1)
.ok_or_else(|| Error::RomBarAddressInvalid(config.addr, config.size))?;
.ok_or(Error::RomBarAddressInvalid(config.addr, config.size))?;
if end_addr > u64::from(u32::max_value()) {
return Err(Error::RomBarAddressInvalid(config.addr, config.size));
@@ -698,7 +698,7 @@ impl PciConfiguration {
};
let end_offset = cap_offset
.checked_add(total_len)
.ok_or_else(|| Error::CapabilitySpaceFull(total_len))?;
.ok_or(Error::CapabilitySpaceFull(total_len))?;
if end_offset > CAPABILITY_MAX_OFFSET {
return Err(Error::CapabilitySpaceFull(total_len));
}
@@ -711,7 +711,7 @@ impl PciConfiguration {
}
self.last_capability = Some((cap_offset, total_len));
if cap_data.id() == PciCapabilityID::MSIX {
if cap_data.id() == PciCapabilityId::MsiX {
self.msix_cap_reg_idx = Some(cap_offset / 4);
}
@@ -768,7 +768,7 @@ impl PciConfiguration {
let value = LittleEndian::read_u32(data);
let mask = self.writable_bits[reg_idx];
if reg_idx >= BAR0_REG && reg_idx < BAR0_REG + NUM_BAR_REGS {
if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) {
let bar_idx = reg_idx - 4;
if (value & mask) != (self.bars[bar_idx].addr & mask) {
// Handle special case where the address being written is
@@ -1005,8 +1005,8 @@ mod tests {
self.as_slice()
}
fn id(&self) -> PciCapabilityID {
PciCapabilityID::VendorSpecific
fn id(&self) -> PciCapabilityId {
PciCapabilityId::VendorSpecific
}
}
@@ -1056,11 +1056,11 @@ mod tests {
}
#[derive(Copy, Clone)]
enum TestPI {
enum TestPi {
Test = 0x5a,
}
impl PciProgrammingInterface for TestPI {
impl PciProgrammingInterface for TestPi {
fn get_register_value(&self) -> u8 {
*self as u8
}
@@ -1074,7 +1074,7 @@ mod tests {
0x1,
PciClassCode::MultimediaController,
&PciMultimediaSubclass::AudioController,
Some(&TestPI::Test),
Some(&TestPi::Test),
PciHeaderType::Device,
0xABCD,
0x2468,

View File

@@ -5,6 +5,7 @@
use crate::configuration::{self, PciBarRegionType};
use std::any::Any;
use std::fmt::{self, Display};
use std::sync::{Arc, Barrier};
use std::{self, io, result};
use vm_allocator::SystemAllocator;
use vm_device::BusDevice;
@@ -63,7 +64,12 @@ pub trait PciDevice: BusDevice {
/// Sets a register in the configuration space.
/// * `reg_idx` - The index of the config register to modify.
/// * `offset` - Offset in to the register.
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]);
fn write_config_register(
&mut self,
reg_idx: usize,
offset: u64,
data: &[u8],
) -> Option<Arc<Barrier>>;
/// Gets a register from the configuration space.
/// * `reg_idx` - The index of the config register to read.
fn read_config_register(&mut self, reg_idx: usize) -> u32;
@@ -82,7 +88,9 @@ pub trait PciDevice: BusDevice {
/// Writes to a BAR region mapped in to the device.
/// * `addr` - The guest address inside the BAR.
/// * `data` - The data to write.
fn write_bar(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
fn write_bar(&mut self, _base: u64, _offset: u64, _data: &[u8]) -> Option<Arc<Barrier>> {
None
}
/// Relocates the BAR to a different address in guest address space.
fn move_bar(&mut self, _old_base: u64, _new_base: u64) -> result::Result<(), io::Error> {
Ok(())

View File

@@ -21,7 +21,7 @@ mod vfio;
pub use self::bus::{PciBus, PciConfigIo, PciConfigMmio, PciRoot, PciRootError};
pub use self::configuration::{
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityID,
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityId,
PciClassCode, PciConfiguration, PciHeaderType, PciMassStorageSubclass,
PciNetworkControllerSubclass, PciProgrammingInterface, PciSerialBusSubClass, PciSubclass,
};

Some files were not shown because too many files have changed in this diff Show More