Compare commits

...

353 Commits

Author SHA1 Message Date
Rob Bradford
d714cf8131 build, release-notes.md: Document 0.9.0 release
Update release notes and version number for the new release.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-06 14:31:36 +01:00
Rob Bradford
8286b316c1 build: Bump vm-memory dependency
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-06 14:26:47 +01:00
Muminul Islam
96caf6b03b rpm: Add spec file for packaging cloud-hypervisor
Packaging the cloud-hyperviosr binary is useful for enterprise use.
This patch adds the spec file for x86_64. Aarch64 support is not added
yet.
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-08-06 09:29:36 +01:00
Sebastien Boeuf
20cd778c5b ci: Retry integration tests if failing
Both gnu and musl workers will retry integration tests up to 3 times if
they fail. This should give us a better pass rate, without having to
restart the entire build every time a single glitch happens.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-08-06 08:29:50 +01:00
dependabot-preview[bot]
7529a9ac05 build(deps): bump seccomp from v0.21.2 to v0.22.0
Bumps [seccomp](https://github.com/firecracker-microvm/firecracker) from v0.21.2 to v0.22.0.
- [Release notes](https://github.com/firecracker-microvm/firecracker/releases)
- [Changelog](cc5387637c/CHANGELOG.md)
- [Commits](a06d358b2e...cc5387637c)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-06 07:25:30 +00:00
Bo Chen
f65372ea4a dev_cli: Remove the use of undefined '$libc' for the 'shell' command
Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-06 08:30:40 +02:00
dependabot-preview[bot]
8e8ec74b2a build(deps): bump clap from 2.33.1 to 2.33.2
Bumps [clap](https://github.com/clap-rs/clap) from 2.33.1 to 2.33.2.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-05 20:36:12 +00:00
dependabot-preview[bot]
1ac6ccb300 build(deps): bump signal-hook-registry from 1.2.0 to 1.2.1
Bumps [signal-hook-registry](https://github.com/vorner/signal-hook) from 1.2.0 to 1.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-08-05 20:35:55 +00:00
Jose Carlos Venegas Munoz
90acb01bad vmm: seccomp: add mprotect to API thread filter
Add mprotect to API thread rules. Prevent the VMM is
killed when it is used.

Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
2020-08-05 21:35:21 +01:00
Rob Bradford
743ebe2fa6 build: Temporarily disable ARM64 builds
The builder is not currently responding to pings.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-05 17:51:20 +01:00
Anatol Belski
4f33ea89cf pci: use vfio-bindings from crates.io
This fixes `cargo vendor` throwing an error

```
$ cargo vendor
error: failed to sync

Caused by:
  found duplicate version of package `vfio-bindings v0.2.0` vendored from two sources:

	source 1: https://github.com/rust-vmm/vfio-bindings#f08cbcbf
	source 2: registry `https://github.com/rust-lang/crates.io-index`
```

Both sources are indeed same, the conflict is only cause by the
different URLs.

Signed-off-by: Anatol Belski <ab@php.net>
2020-08-05 17:50:45 +01:00
Sebastien Boeuf
0f1ab38ded hypervisor: kvm: Make MSRs set/get more flexible
Based on the way KVM_GET_MSRS and KVM_SET_MSRS work, both function are
very unlikely to fail, as they simply stop looping through the list of
MSRs as soon as getting or setting one fails. This is causing some
issues with the snapshot/restore feature, as on some platforms, we only
save a subset of the list of MSRs, leading to unproper way of saving the
VM.

The way to address this issue is by checking the number of MSRs get/set
matches the expected amount from the list. In case it does not match, we
simply ignore the failing MSR and continue getting/setting the rest of
the list. By doing this by iterations, we end up getting/setting as many
MSRs as the platform can support.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-08-05 14:52:35 +01:00
Rob Bradford
44cf97e2fd build: Fix Jenkinsfile syntax
Turns out that "when" blocks are permitted but not effectual in "post"
sections so instead use a script behaviour to make the message
conditional on build change.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-05 14:49:33 +01:00
Rob Bradford
b5d64be479 virtio-devices: iommu: Port to EpollHelper
Migrate to EpollHelper so as to remove code that is duplicated between
multiple virtio devices.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-05 13:15:23 +02:00
Rob Bradford
2e98208af5 build: Alert on Slack when master build regresses or is fixed
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-05 13:14:20 +02:00
Rob Bradford
55c16fecbf virtio-devices: seccomp: Add missing dup() syscalls
The refactoring to use EpollHelper added a requirement on this system
call.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-05 11:32:31 +02:00
Rob Bradford
0e335a709d virtio-devices: Print out worker error messages
Currently any messages generated during the worker thread are not
shown anywhere as the thread is never join()ed on. Instead output the
error immediately.

For now only cover the subset where the work to port to EpollHandler
clashed with the seccomp filtering for virtio devices.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-05 11:32:31 +02:00
dependabot-preview[bot]
ec9de259ba build(deps): bump seccomp from v0.21.1 to v0.21.2
Bumps [seccomp](https://github.com/firecracker-microvm/firecracker) from v0.21.1 to v0.21.2.
- [Release notes](https://github.com/firecracker-microvm/firecracker/releases)
- [Changelog](a06d358b2e/CHANGELOG.md)
- [Commits](047a379eb0...a06d358b2e)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-05 07:34:44 +00:00
Michael Zhao
fc2d9c6e31 ci: Remove unused image in sha1sums-aarch64
We no longer download focal-server-cloudimg-arm64.img, checking it
lead to a failure if "work_loads" folder is new.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-08-05 08:15:18 +01:00
Michael Zhao
f2db346127 tests: Enable more integration test cases on AArch64
Enabled 3 test cases:
- test_large_vm
- test_huge_memory
- test_serial_null

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-08-05 08:15:18 +01:00
Bo Chen
dc71d2765a virtio-devices: seccomp: Add seccomp filters for pmem thread
This patch enables the seccomp filters for the pmem worker thread.

Partially fixes: #925

Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-05 08:13:31 +01:00
Bo Chen
d77977536d virtio-devices: seccomp: Add seccomp filters for net thread
This patch enables the seccomp filters for the net worker thread.

Partially fixes: #925

Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-05 08:13:31 +01:00
Bo Chen
276df6b71c virtio-devices: seccomp: Add seccomp filters for console thread
This patch enables the seccomp filters for the console worker thread.

Partially fixes: #925

Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-05 08:13:31 +01:00
Bo Chen
a426221167 virtio-devices: seccomp: Add seccomp filters for rng thread
This patch enables the seccomp filters for the rng worker thread.

Partially fixes: #925

Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-05 08:13:31 +01:00
Bo Chen
6a26789222 virtio-devices: seccomp: Fix typos in the block module
Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-05 08:13:31 +01:00
dependabot-preview[bot]
3eeee63189 build(deps): bump syn from 1.0.37 to 1.0.38
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.37 to 1.0.38.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.37...1.0.38)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-05 06:43:20 +00:00
Rob Bradford
ce65093f8d virtio-devices: pmem: Port to EpollHelper
Migrate to EpollHelper so as to remove code that is duplicated between
multiple virtio devices.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-05 08:43:01 +02:00
Rob Bradford
e093f0e83e virtio-devices: rng: Port to EpollHelper
Migrate to EpollHelper so as to remove code that is duplicated between
multiple virtio devices.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-05 08:42:06 +02:00
Ricardo Koller
358b3c0b89 Dummy change to start the tests
Signed-off-by: Ricardo Koller <ricarkol@gmail.com>
2020-08-05 08:38:58 +02:00
Ricardo Koller
7589f1b3bf vhost_user_fs: increase RLIMIT_NOFILE
Increase the number of open files limit for the sandboxed process to the
maximum allowed in the system. The maximum is obtained by reading the
/proc/sys/fs/nr_open sysctl file, and the setting is done using the setrlimit
syscall. Failure to read or parse the nr_open file, or to set the rlimit
results in a panic.

Signed-off-by: Ricardo Koller <ricarkol@gmail.com>
2020-08-05 08:38:58 +02:00
Rob Bradford
8f39b5ef84 build: Update Cargo.lock for kvm-bindings
Point to the non-merge commit.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-08-04 16:50:06 +01:00
Henry Wang
a47da51539 ci: AArch64: Enable basic virtio_vsock test case for AArch64
This commit enables the test case for testing the basic function
of virtio_vsock (i.e. without the hotplug).

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-08-04 13:16:14 +01:00
Bo Chen
704edd544c virtio-devices: seccomp: Add seccomp_filter module
This patch added the seccomp_filter module to the virtio-devices crate
by taking reference code from the vmm crate. This patch also adds
allowed-list for the virtio-block worker thread.

Partially fixes: #925

Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-04 11:40:49 +02:00
Bo Chen
ff7ed8f628 vmm: Propagate the SeccompAction value to the Vm struct constructor
This patch propagates the SeccompAction value from main to the
Vm struct constructor (i.e. Vm::new_from_memory_manager), so that we can
use it to construct the DeviceManager and CpuManager struct for
controlling the behavior of the seccomp filters for vcpu/virtio-device
worker threads.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-04 11:40:49 +02:00
Bo Chen
8e74637ebb main, vmm: seccomp: Add the '--seccomp log' option
This patch extends the CLI option '--seccomp' to accept the 'log'
parameter in addition 'true/false'. It also refactors the
vmm::seccomp_filters module to support both "SeccompAction::Trap" and
"SeccompAction::Log".

Fixes: #1180

Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-04 11:40:49 +02:00
Bo Chen
b41884a406 main, vmm: seccomp: Use SeccompAction instead of SeccompLevel
This patch replaces the usage of 'SeccompLevel' with 'SeccompAction',
which is the first step to support the 'log' action over system
calls that are not on the allowed list of seccomp filters.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-08-04 11:40:49 +02:00
dependabot-preview[bot]
bfc37bc8d3 build(deps): bump syn from 1.0.36 to 1.0.37
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.36 to 1.0.37.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.36...1.0.37)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-04 06:44:59 +00:00
Henry Wang
61c093f724 ci: AArch64: Enable api_create_boot for AArch64
This commit enables the `api_create_boot` case in the integration
test as the test for the Cloud Hypervisor API server functionality.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-08-03 14:15:14 +01:00
Sebastien Boeuf
8f0bf82648 io_uring: Add new feature gate
By adding a new io_uring feature gate, we let the user the possibility
to choose if he wants to enable the io_uring improvements or not.
Since the io_uring feature depends on the availability on recent host
kernels, it's better if we leave it off for now.

As soon as our CI will have support for a kernel 5.6 with all the
features needed from io_uring, we'll enable this feature gate
permanently.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-08-03 14:15:01 +01:00
Sebastien Boeuf
a85304196e virtio-devices: Improve error handling for virtio-blk io_uring
Instead of just logging error messages but continue the processing of
the queues, this patch returns errors right away. This allows for a
quicker detection of an error happening on the virtqueue.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-08-03 14:15:01 +01:00
Sebastien Boeuf
917027c55b vmm: Rely on virtio-blk io_uring when possible
In case the host supports io_uring and the specific io_uring options
needed, the VMM will choose the asynchronous version of virtio-blk.
This will enable better I/O performances compared to the default
synchronous version.

This is also important to note the VMM won't be able to use the
asynchronous version if the backend image is in QCOW format.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-08-03 14:15:01 +01:00
Sebastien Boeuf
64283726e7 virtio-devices: Add an asynchronous virtio-blk device
This introduces a new version of virtio-blk device. The default
virtio-blk provides synchronous processing of the queues, while this
new version relies on io_uring from the host kernel to provide an
asynchronous processing of the queues.

This new asynchronous version provides a huge performance improvement
compared to the default synchronous version.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-08-03 14:15:01 +01:00
Sebastien Boeuf
49a6500185 block_util: Add utilities to support io_uring
Creates a dedicated function relying on io_uring crate to execute
io_uring specific requests.

Also creates a function for checking io_uring support on the host.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-08-03 14:15:01 +01:00
Henry Wang
5807a91f33 scripts: AArch64: Fix abnormal integration script behaviour
PR#1511 introduced a `flock` command in order to let AArch64 CI
can be executed with multiple executors. However the command
```
(
    echo "try to lock $WORKLOADS_DIR folder and update"
    flock -x 12 && update_workloads
)
```
will introduce an abnormal behavior: If any error happened in
function `updated_workloads`, the sub-shell opened by the pair of
parentheses will be killed instead of the main shell, which is not
right.

This commit fixes this abnormal execution behaviour.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-07-31 14:08:15 +02:00
Henry Wang
d5863caa4d ci: AArch64: Enable virtio-fs integration test
This commit enables the virtio-fs related integration test cases
for AArch64.

Note that to run virtio-fs cases, the host kernel should be
newer than v5.5.

Fixes: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1516

Signed-off-by: Henry Wang <henry.wang@arm.com>
2020-07-31 14:06:55 +02:00
Henry Wang
77ba041362 ci: AArch64: Update custom linux kernel to v5.8-rc4
This commit updates the AArch64 kernel config file and integration
test script to v5.8-rc4, and this update keeps the aarch64 guest
kernel in sync with the x86_64 one.

Fixes: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1516

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-07-31 14:06:55 +02:00
Michael Zhao
44eccbe1af scripts: Remove the workaround for "with-serde" build error on AArch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-31 12:35:17 +01:00
Praveen Paladugu
afa8ecc90c vmm: add validation for network parameters
Signed-off-by: Praveen Paladugu <prapal@microsoft.com>
2020-07-31 09:07:12 +02:00
Wei Liu
a52b614a61 vmm: device_manager: console input should be only consumed by one device
Cloud Hypervisor allows either the serial or virtio console to output to
TTY, but TTY input is pushed to both.

This is not correct. When Linux guest is configured to spawn TTYs on
both ttyS0 and hvc0, the user effectively issues the same commands twice
in different TTYs.

Fix this by only direct input to the one choice that is using host side
TTY.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-30 18:05:01 +02:00
Wei Liu
5ed794a44c vmm: device_manager: rename console_input to virtio_console_input
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-30 18:05:01 +02:00
Michael Zhao
fec54f71b0 tests: Enable PCI integration test cases on AArch64
Enabled a minimum set of PCI test case.
More cases are to be adapted or debugged.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-30 09:52:12 +02:00
Michael Zhao
8e520d2415 resource: Enable PCI controller driver in AArch64 kernel config
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-30 09:52:12 +02:00
Wei Liu
3e68867bb7 vmm: device_manager: eliminate KvmMsiInterruptManager from the new function
The logic to create an MSI interrupt manager is applicable to Hyper-V as
well.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-30 08:00:33 +02:00
Michael Zhao
cf1b5156f4 scripts: Change AArch64 container network type BRIDGE
Changed the container network type to BRIDGE to seprate the networking
of parallel containers.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-29 18:32:44 +01:00
Michael Zhao
7e3cbf04de scripts: Improve AArch64 CI for parallel executors
Lock "work_loads" folder when one job is syncing files. If another job
arrives, wait until the lock is released.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-29 18:32:44 +01:00
dependabot-preview[bot]
12c5b7668a build(deps): bump libc from 0.2.73 to 0.2.74
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.73 to 0.2.74.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.73...0.2.74)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-28 20:46:37 +00:00
Muminul Islam
d49059a31b README: Fix copy command when using config from Cloud-hypervisor
The command which is mentioned in the README is wrong. We have two
cofigs one for x86_64 and another for aarh64. Previously it was a single
config. After adding the configs the read me was not modified. This
patch fixes the issue.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-07-28 21:09:22 +01:00
Wei Liu
218ec563fc vmm: fix warnings when KVM is not enabled
Some imports are only used by KVM. Some variables and code become dead
or unused when KVM is not enabled.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-28 21:08:39 +01:00
Howard Zhang
15bf61c6aa doc: AArch64: align rootfs name in arm64.md
Signed-off-by: Howard Zhang <howard.zhang@arm.com>
2020-07-28 10:38:13 +08:00
dependabot-preview[bot]
58f1051278 build(deps): bump pin-project from 0.4.22 to 0.4.23
Bumps [pin-project](https://github.com/taiki-e/pin-project) from 0.4.22 to 0.4.23.
- [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/v0.4.22...v0.4.23)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 14:22:17 +00:00
dependabot-preview[bot]
35578f766a build(deps): bump proc-macro-hack from 0.5.16 to 0.5.18
Bumps [proc-macro-hack](https://github.com/dtolnay/proc-macro-hack) from 0.5.16 to 0.5.18.
- [Release notes](https://github.com/dtolnay/proc-macro-hack/releases)
- [Commits](https://github.com/dtolnay/proc-macro-hack/compare/0.5.16...0.5.18)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 13:08:25 +00:00
Sebastien Boeuf
8ad0e4d715 ci: Move to latest virtiofsd version
In order to follow the virtio-fs development, we rely on the latest
development branch regarding the virtio-fs daemon. It will be now build
from virtio-fs-dev branch from the virtio-fs maintainers repository.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-27 14:03:05 +01:00
Sebastien Boeuf
8dc0f49ba5 deps: Update vhost dependency
Move to latest vhost version, as it contains the updated list of
vhost-user protocol features along with the updated list of virtio-fs
slave commands.

This will allow Cloud-Hypervisor to work with latest virtiofsd binary.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-27 14:03:05 +01:00
dependabot-preview[bot]
6bf0d381e1 build(deps): bump serde_json from 1.0.56 to 1.0.57
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.56 to 1.0.57.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.56...v1.0.57)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 12:12:14 +00:00
Jianyong Wu
d24b110519 seccomp: AArch64: Add SYS_unlinkat to seccomp whitelist
This commit fixes an "Bad syscall" error when shutting down the VM
on AArch64 by adding the SYS_unlinkat syscall to the seccomp
whitelist.

Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2020-07-27 07:25:07 +00:00
dependabot-preview[bot]
948cd20bfa build(deps): bump syn from 1.0.35 to 1.0.36
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.35 to 1.0.36.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.35...1.0.36)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-27 06:21:51 +00:00
dependabot-preview[bot]
9eb6d45c40 build(deps): bump anyhow from 1.0.31 to 1.0.32
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.31 to 1.0.32.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.31...1.0.32)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-25 08:59:42 +00:00
Rob Bradford
56bfe2700d virtio-devices, net_util: Migrate virtio-net to EpollHelper
EpollHelper allows the removal of much duplicated loop handling code and
instead the device specific even handling is delegated via an
implementation of EpollHelperHandler.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-24 13:50:37 +02:00
Rob Bradford
d66fa942be virtio-devices: Implement AsRawFd for EpollHelper
This is required for implementing virtio-net as the epoll RawFd must be
assigned into the NetQueuePair.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-24 13:50:37 +02:00
Rob Bradford
1dcf1b11e4 virtio-devices: Reserve first 16 events for the EpollHelper
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-24 13:50:37 +02:00
dependabot-preview[bot]
609f34fa43 build(deps): bump micro_http from c9ffb90 to 40309b7
Bumps [micro_http](https://github.com/firecracker-microvm/micro-http) from `c9ffb90` to `40309b7`.
- [Release notes](https://github.com/firecracker-microvm/micro-http/releases)
- [Commits](c9ffb90aeb...40309b72ee)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-24 06:42:53 +00:00
Rob Bradford
9ae44aeada vmm: acpi_tables: Fix PM timer I/O port width
Ensure that the width of the I/O port is correctly set to 32-bits in the
generic address used for the X_PM_TMR_BLK. Do this by type
parameterising GenericAddress::io_port_address() fuction.

TEST=Boot with clocksource=acpi_pm and observe no errors in the dmesg.

Fixes: #1496

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-23 17:48:22 +02:00
Rob Bradford
5cad2334dd virtio-block: Port to EpollHelper
Port virtio-block device to the new EpollHelper. This required moving
the queue EventFd ownership to BlockEpollHandler.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-23 13:10:34 +01:00
Rob Bradford
7d9dc4013e virtio-devices: Introduce EpollHelper
This is a helper for implementing the worker thread for virtio devices
and in particular handles special behaviour for pause and kill events.

The device specific event handling (for the queues themselves) is
delegated to a method invoked on a new EpollHelperHandler trait. This
method is passed the event as well as the EpollHelper so that it can
operate on the handler in order to manage events itself (required for
virtio-net.)

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-23 13:10:34 +01:00
Rob Bradford
aae5d988e1 devices: vmm: Add ACPI PM timer
This is a counter exposed via an I/O port that runs at 3.579545MHz. Here
we use a hardcoded I/O and expose the details through the FADT table.

TEST=Boot Linux kernel and see the following in dmesg:

[    0.506198] clocksource: acpi_pm: mask: 0xffffff max_cycles: 0xffffff, max_idle_ns: 2085701024 ns

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-23 13:10:21 +01:00
Sebastien Boeuf
4fc7eb3daa docs: Add snapshot/restore documentation
Add some documentation regarding the snapshot/restore feature which was
introduced earlier, explaining how the feature works and how to use it
properly.

Fixes #1490

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-22 12:49:34 +01:00
Rob Bradford
08288305ef devices: cmos: Populate Update in Progress bit (UIP)
This bit should be set for the last 224us of each second. To be able to
fill this bit we need to use clock_gettime() to get the nanoseconds.
Also set the frequency bit to indicate we update at 32kHz.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-22 09:57:46 +02:00
Wei Liu
f03afea0d6 device_manager: document unsafe block in add_vfio_device
It is not immediately obvious why the conversion is safe. Document the
safety guarantee.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-21 17:13:10 +01:00
Samuel Ortiz
be51ea250d device_manager: Simplify the passthrough internal API
We store the device passthrough handler, so we should use it through our
internal API and only carry the passed through device configuration.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-07-21 17:20:25 +02:00
Michael Zhao
f57d970451 arch: Code cleanup after refactoring aarch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-21 16:22:02 +02:00
Michael Zhao
ddf1b76906 hypervisor: Refactor create_passthrough_device() for generic type
Changed the return type of create_passthrough_device() to generic type
hypervisor::Device.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-21 16:22:02 +02:00
Michael Zhao
6c8749adf2 arch: Switch to hypervisor crate
Removed the dependency on kvm-bindings and kvm-ioctls, use hypervisor
crate instead.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-21 16:22:02 +02:00
Michael Zhao
e7288888cf hypervisor: Extend hypervisor crate with Device trait
Added Device trait and KvmDevice struct for KVM-emulated devices.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-21 16:22:02 +02:00
Michael Zhao
e3e771727a arch: Refactor GIC code to seperate KVM specific code
Shrink GICDevice trait to contain hypervisor agnostic API's only, which
are used in generating FDT.
Move all KVM specific logic into KvmGICDevice trait.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-21 16:22:02 +02:00
Rob Bradford
6e8c8991d9 fuzz: qcow: Remove unnecessary "use libc"
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-20 20:22:47 +02:00
Rob Bradford
191f64028a fuzz: Add a fuzzer for virtio-devices::Block
This is based on the block fuzzer from crosvm and ported to Cloud
Hypervisor.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-20 20:22:47 +02:00
dependabot-preview[bot]
d922986d78 build(deps): bump syn from 1.0.34 to 1.0.35
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.34 to 1.0.35.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.34...1.0.35)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-20 17:38:11 +00:00
Michael Zhao
b363d1c706 tests: Enable integration test for initramfs on AArch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-20 14:20:53 +01:00
Michael Zhao
3e051e7b2c arch, vmm: Enable initramfs on AArch64
Ported Firecracker commit 144b6c.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-20 14:20:53 +01:00
Henry Wang
f449aec78e ci: AArch64: Enable AArch64 mmio-related integration test cases
This commit enables some mmio-related integration test cases on
AArch64, including:
* some vhost_user test cases
* virtio-blk test cases
* pmem test cases

Also this commit contains a bug fix in creating virtio-blk device.
Previously, when creating the FDT, the virtio-blk device was
labeled in the reverse order of address allocation.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-07-20 11:45:47 +01:00
dependabot-preview[bot]
07b6026f45 build(deps): bump proc-macro2 from 1.0.18 to 1.0.19
Bumps [proc-macro2](https://github.com/alexcrichton/proc-macro2) from 1.0.18 to 1.0.19.
- [Release notes](https://github.com/alexcrichton/proc-macro2/releases)
- [Commits](https://github.com/alexcrichton/proc-macro2/compare/1.0.18...1.0.19)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-20 08:06:29 +00:00
Sebastien Boeuf
840445096a docs: Add Intel SGX documentation
Adding a few notes and links about how to enable and use SGX through
Cloud-Hypervisor.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-20 09:06:06 +01:00
Wei Liu
e1af251c9f vmm, hypervisor: adjust set_gsi_routing / set_gsi_routes
Make set_gsi_routing take a list of IrqRoutingEntry. The construction of
hypervisor specific structure is left to set_gsi_routing.

Now set_gsi_routes, which is part of the interrupt module, is only
responsible for constructing a list of routing entries.

This further splits hypervisor specific code from hypervisor agnostic
code.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-20 07:32:32 +02:00
dependabot-preview[bot]
12b37ef13b build(deps): bump libc from 0.2.72 to 0.2.73
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.72 to 0.2.73.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.72...0.2.73)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-20 05:15:24 +00:00
Wei Liu
d484a3383c vmm: device_manager: introduce add_passthrough_device
It calls add_vfio_device on KVM or returns an error when not running on
KVM.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-17 20:21:39 +02:00
Wei Liu
821892419c vmm: device_manager: use generic names for passthrough device
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-17 20:21:39 +02:00
Wei Liu
ff8d7bfe83 hypervisor: add create_passthrough_device call to Vm trait
That function is going to return a handle for passthrough related
operations.

Move create_kvm_device code there.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-17 20:21:39 +02:00
Samuel Ortiz
7bfe87b7af README: Refer to the community repository
We now have a defined governance model.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-07-17 12:56:37 +02:00
Rob Bradford
4d878418e5 fuzz: Add fuzzing infrastructure and QCOW fuzzer
Add the basic infrastructure for fuzzing along with a qcow fuzzer ported
from crosvm and adapted to our code.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-17 08:49:35 +02:00
dependabot-preview[bot]
9267628e7c build(deps): bump vfio-ioctls from 195d6c6 to 49cc362
Bumps [vfio-ioctls](https://github.com/cloud-hypervisor/vfio-ioctls) from `195d6c6` to `49cc362`.
- [Release notes](https://github.com/cloud-hypervisor/vfio-ioctls/releases)
- [Commits](195d6c6eb0...49cc3626f6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-17 06:45:58 +00:00
Wei Liu
c08d2b2c70 device_manager: avoid manipulating MemoryRegion fields directly
Hyper-V may have different field names. Use make_user_memory_region
instead.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-16 15:56:03 +02:00
Rob Bradford
dfd6f3471d virtio-devices: vhost_user: block: Only change mutable field in write_config()
The only driver writable field in the virtio-block specification is the
writeback one. Check that the offset being written to is for that field
and update it.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
eabcd618ba virtio-devices: block: Only change mutable field in write_config()
The only driver writable field in the virtio-block specification is the
writeback one. Check that the offset being written to is for that field
and update it.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
751a302050 virtio-devices: Port devices over to new read_config_from_slice() helper
Using this helper removes lots of duplicated code across the devices.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
d262540857 virtio-devices: Introduce helper for implementing read_config()
Add a helper function to share code between implementations that can use
a slice accessible data structure for configuration data.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
5d67338b29 virtio-devices: Remove warning generating read_config() implementation
The version in VirtioDevice can be used instead.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
676322c3cc virtio-devices: vsock: Remove write_config() implementation
This warning generating implementation can be handled by the version in
VirtioDevice.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
580739f776 virtio-devices: vhost_user: fs: Remove write_config() implementation
All the configuration fields for virtio-fs are read only for the driver.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
15d5cc62ff virtio-devices: vhost_user: net: Remove write_config implementation
The spec specifies that all fields are read-only from the driver.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
f1e940df8d virtio-devices: net: Remove write_config implementation
The spec specifies that all fields are read-only from the driver.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
e3dc620146 virtio-devices: Remove write_config implementations
Remove the write_config() implementations that only generate a warning
as that is now done at the VirtioDevice level.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Rob Bradford
6ba1c4318e virtio-devices: Add default implementations for {read,write}_config
Not every virtio device has any config fields that can be read and most
have none that can be written to.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-16 13:17:22 +02:00
Michael Zhao
f1419d116d docs: Update AArch64 doc to use KVM feature
Updated the build instructions to adapt to new added KVM feature.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-16 11:00:32 +02:00
Michael Zhao
2de4f73275 arch: Add log messages in GIC creation
The retry order to create virtual GIC is GICv3-ITS, GICv3 and GICv2.
But there was not log message to show what was finally created.
The log messages also mute the warning for unused "log" crate.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-16 11:00:32 +02:00
Bo Chen
48966b4535 docs: Update API documentation about '/vm.remove-device'
With recent changes (#1030 #1084), the '/vm.remove-device' API is no
longer restricted to remove VFIO PCI devices. It now supports (almost)
all hotpluggable devices, e.g. disk/net/pmem/fs/vsock.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2020-07-16 07:35:26 +02:00
Muminul Islam
053ea5dcd3 vfio: Make vfio to use MemoryRegion instead of kvm_userspace_memory_region
Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-07-16 07:34:27 +02:00
dependabot-preview[bot]
cc57467d10 build(deps): bump log from 0.4.8 to 0.4.11
Bumps [log](https://github.com/rust-lang/log) from 0.4.8 to 0.4.11.
- [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.8...0.4.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-16 05:33:44 +00:00
Wei Liu
d80e383dbb arch: move test cases to vmm crate
This saves us from adding a "kvm" feature to arch crate merely for the
purpose of running tests.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-15 17:21:07 +02:00
Wei Liu
598eaf9f86 vmm: use hypervisor::new in test_vm
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-15 17:21:07 +02:00
Wei Liu
11ffc83365 arch: use hypervsisor::new in test cases
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-15 17:21:07 +02:00
Wei Liu
085d165f8a bin: switch to hypervisor::new
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-15 17:21:07 +02:00
Wei Liu
a83bd97e0d hypervisor: introduce a new function
It returns an hypervisor object depending on which hypervisor is
configured.  Currently it only supports KVM.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-15 17:21:07 +02:00
Sebastien Boeuf
a5c4f0fc6f arch, vmm: Add e820 entry related to SGX EPC region
SGX expects the EPC region to be reported as "reserved" from the e820
table. This patch adds a new entry to the table if SGX is enabled.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-15 15:08:56 +02:00
Sebastien Boeuf
e10d9b13d4 arch, hypervisor, vmm: Patch CPUID subleaves to expose EPC sections
The support for SGX is exposed to the guest through CPUID 0x12. KVM
passes static subleaves 0 and 1 from the host to the guest, without
needing any modification from the VMM itself.

But SGX also relies on dynamic subleaves 2 through N, used for
describing each EPC section. This is not handled by KVM, which means
the VMM is in charge of setting each subleaf starting from index 2
up to index N, depending on the number of EPC sections.

These subleaves 2 through N are not listed as part of the supported
CPUID entries from KVM. But it's important to set them as long as index
0 and 1 are present and indicate that SGX is supported.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-15 15:08:56 +02:00
Sebastien Boeuf
1603786374 vmm: Pass MemoryManager through CpuManager creation
Instead of passing the GuestMemoryMmap directly to the CpuManager upon
its creation, it's better to pass a reference to the MemoryManager. This
way we will be able to know if SGX EPC region along with one or multiple
sections are present.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-15 15:08:56 +02:00
Sebastien Boeuf
2b06ce0ed4 vmm: Add EPC device to ACPI tables
The SGX EPC region must be exposed through the ACPI tables so that the
guest can detect its presence. The guest only get the full range from
ACPI, as the specific EPC sections are directly described through the
CPUID of each vCPU.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-15 15:08:56 +02:00
Sebastien Boeuf
84cf12d86a arch, vmm: Create SGX virtual EPC sections from MemoryManager
Based on the presence of one or multiple SGX EPC sections from the VM
configuration, the MemoryManager will allocate a contiguous block of
guest address space to hold the entire EPC region. Within this EPC
region, each EPC section is memory mapped.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-15 15:08:56 +02:00
Sebastien Boeuf
d9244e9f4c vmm: Add option for enabling SGX EPC regions
Introducing the new CLI option --sgx-epc along with the OpenAPI
structure SgxEpcConfig, so that a user can now enable one or multiple
SGX Enclave Page Cache sections within a contiguous region from the
guest address space.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-15 15:08:56 +02:00
Rob Bradford
1842865823 build: Add missing "hypervisor" crate from workspace
Also rearrange the workspace members so they are in alphabetical order.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-15 13:09:21 +02:00
Rob Bradford
9c867edc35 vhost_user_block, virtio-devices, block_util: Extract common block code
Extract the code that is used by vhost_user_block from the
virtio-devices crate to remove the dependencies on unrequired
functionality such as the virtio transports.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-15 10:17:11 +01:00
dependabot-preview[bot]
ce79482ec4 build(deps): bump linux-loader from ec930d7 to b309c9d
Bumps [linux-loader](https://github.com/rust-vmm/linux-loader) from `ec930d7` to `b309c9d`.
- [Release notes](https://github.com/rust-vmm/linux-loader/releases)
- [Commits](ec930d700f...b309c9df17)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-15 07:03:06 +00:00
Henry Wang
5a2ff98917 ci: AArch64: Add a build stage for musl toolchain
This commit adds required environment configurations to the
`dev_cli.sh` and a Jenkins stage to enable AArch64 binary
building using musl toolchain.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-07-15 08:19:15 +02:00
Henry Wang
e81402e76e ci: AArch64: Refactor custom linux kernel building strategy
As the current AArch64 integration test builds kernel every time,
which would take unnecessary time in CI and therefore not ideal.

This commit refactors the AArch64 kernel building strategy to:
1. Keep the Linux kernel source code directory instead of deleting
it everytime after the kernel is built.

2. In integration test script, check if the Linux kernel source
code directory exists. If so use `git fetch && git checkout -f` to
keep the source code always updated, else run `git clone` to get
the source code.

3. Copy config file in and then compile the kernel.

Fixes: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1444

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-07-15 08:10:20 +02:00
Rob Bradford
dc55e45977 pci: Introduce and use PciBar struct
This simplies some of the handling for PCI BARs particularly with
respect to snapshot and restore. No attempt has been made to handle the
64-bit bar handling in a different manner to that which was used before.

Fixes: #1153

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-15 07:59:33 +02:00
Michael Zhao
cbb1f4cc2d docs: Add document for using PCI on AArch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-14 14:34:54 +01:00
Michael Zhao
cce6237536 pci: Enable GSI routing (MSI type) for AArch64
In this commit we saved the BDF of a PCI device and set it to "devid"
in GSI routing entry, because this field is mandatory for GICv3-ITS.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-14 14:34:54 +01:00
Michael Zhao
82a0e29c7a hypervisor: Export check_extension() API from hypervisor::Vm
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-14 14:34:54 +01:00
Michael Zhao
f2e484750a arch: aarch64: Add PCIe node in FDT for AArch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-14 14:34:54 +01:00
Michael Zhao
598bcf1459 arch: aarch64: Add GICv3-ITS on AArch64
GICv3-ITS is needed for MSI handling.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-14 14:34:54 +01:00
Michael Zhao
17057a0dd9 vmm: Fix build errors with "pci" feature on AArch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-14 14:34:54 +01:00
dependabot-preview[bot]
9ec4aa4019 build(deps): bump vfio-ioctls from 4c56812 to 195d6c6
Bumps [vfio-ioctls](https://github.com/cloud-hypervisor/vfio-ioctls) from `4c56812` to `195d6c6`.
- [Release notes](https://github.com/cloud-hypervisor/vfio-ioctls/releases)
- [Commits](4c568127fe...195d6c6eb0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 16:53:05 +00:00
Wei Liu
e5552a53d8 arch, pci: rename vm_fd to vm
The type is now hypervisor::Vm. Switch from KVM specific name vm_fd to a
generic name just like 8186a8eee6 ("vmm: interrupt: Rename vm_fd").

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-13 16:08:00 +01:00
Sebastien Boeuf
8be80aca80 ci: Give some time to the disk hotplug
Under high load, the VM might take some time to hotplug the disk after
the hotplug command has been issued. For this reason, let's put a 10s
sleep before checking for the presence of the new disk.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-13 12:19:24 +02:00
Sebastien Boeuf
802f02532a ci: Extend test_vfio time before VM is ready
We want to give the time to the nested VM to be fully ready before we
check it's correctly setup. This involves 3 layers of virtualization
when running the CI on Azure, which added to the high load happening
because of the parallelization, adds up to the start up time.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-13 12:19:24 +02:00
Hui Zhu
165568e98b virtio-mem: Update VirtioMemConfig according to Linux kernel
Types of block_size and padding is updated in
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=544fc7dbbf920a3e64d109c416ee229e8e1763c5.
Update VirtioMemConfig according to it.

Signed-off-by: Hui Zhu <teawater@antfin.com>
2020-07-13 12:19:24 +02:00
Sebastien Boeuf
3cc8ca4ea5 virtio-devices: Update virtio-iommu to follow 5.8-rc4 kernel
The virtio configuration structures have been slightly modified between
5.6-rc4 and 5.8-rc4, forcing the virtio-iommu device to be updated
accordingly.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-13 12:19:24 +02:00
Sebastien Boeuf
2721a7004f ci: Update guest kernel to v5.8-rc4
Updating the kernel from 5.6-rc4 to 5.8-rc4 allows us to remove the
dependency on both virtio-vsock and virtio-mem patches as they are now
part of the upstream kernel. We're still carrying virtio-iommu and
virtio-fs patches.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-13 12:19:24 +02:00
Rob Bradford
81ce8f505c tests: Enable virtio-mmio tests for virtio-console
This is useful to ensure that virtio-console support is covered.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-13 10:16:03 +02:00
dependabot-preview[bot]
3800acd347 build(deps): bump adler from 0.2.2 to 0.2.3
Bumps [adler](https://github.com/jonas-schievink/adler) from 0.2.2 to 0.2.3.
- [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/v0.2.2...v0.2.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 05:31:14 +00:00
dependabot-preview[bot]
cde162c62f build(deps): bump syn from 1.0.33 to 1.0.34
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.33 to 1.0.34.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.33...1.0.34)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 05:06:39 +00:00
dependabot-preview[bot]
8ab11ae87e build(deps): bump pkg-config from 0.3.17 to 0.3.18
Bumps [pkg-config](https://github.com/rust-lang/pkg-config-rs) from 0.3.17 to 0.3.18.
- [Release notes](https://github.com/rust-lang/pkg-config-rs/releases)
- [Changelog](https://github.com/rust-lang/pkg-config-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/pkg-config-rs/compare/0.3.17...0.3.18)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 05:05:29 +00:00
dependabot-preview[bot]
dda4cd8eb7 build(deps): bump redox_syscall from 0.1.56 to 0.1.57
Bumps redox_syscall from 0.1.56 to 0.1.57.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-13 05:04:16 +00:00
Rob Bradford
4963e37dc8 qcow, virtio-devices: Break cyclic dependency
Move the definition of RawFile from virtio-devices crate into qcow
crate. All the code that consumes RawFile also already depends on the
qcow crate for image file type detection so this change breaks the
need for the qcow crate to depend on the very large virtio-devices
crate.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-10 17:47:31 +02:00
Rob Bradford
6161d3bef8 tests: Enable virtio-vsock testing with virtio-mmio transport
As compiling without acpi (implied by mmio) means that the VM will
terminate on i8042 reset we cannot test the reboot.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-10 07:22:37 +02:00
Rob Bradford
bb6b738c0d tests: Enable test_multiple_network_interfaces with virtio-mmio
Ensure that multiple network interfaces function correctly when using
virtio-mmio.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-09 09:53:30 +02:00
Rob Bradford
730f3884f4 tests: Enable test_virtio_blk with virtio-mmio transport
This test makes use of MQ support in virtio-block so it is advisable to
test with virtio-mmio.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-09 09:53:13 +02:00
Rob Bradford
d725c2cbfb tests: Enable virtio-mem testing with virtio-mmio
Try and enable virtio-mem testing when using the virtio-mmio transport.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-09 09:52:56 +02:00
dependabot-preview[bot]
96274a33ca build(deps): bump cc from 1.0.57 to 1.0.58
Bumps [cc](https://github.com/alexcrichton/cc-rs) from 1.0.57 to 1.0.58.
- [Release notes](https://github.com/alexcrichton/cc-rs/releases)
- [Commits](https://github.com/alexcrichton/cc-rs/compare/1.0.57...1.0.58)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-08 15:12:19 +00:00
Rob Bradford
f47f221f24 build: Remove obsolete feature differentiated vhost_user_net builds
Now that vhost_user_net crate does not depend on the virtio-devices
crate it is no longer compiled differently based on the mmio or pci
features.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-08 16:11:50 +01:00
Rob Bradford
17766fcea4 net_util, vhost_user_net, virtio-devices: Move NetQueuePair
Move NetQueuePair and the related NetCounters into the net_util crate.
This means that the vhost_user_net crate now no longer depends on
virtio-devices and so does not depend on the pci, qcow or other similar
crates. This significantly simplifies the build chain for this backend.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-08 16:11:50 +01:00
Rob Bradford
1237784a8f net_util, virtio-devices: Move TAP register/unregister helpers
Move these helper functions into net_util so that they can be used from
code inside there.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-08 16:11:50 +01:00
Rob Bradford
7f3e40e0cf net_util: Consolidate vnet_hdr_len() functions
As a result of refactoring this function was duplicated so consolidate
into the top level.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-08 16:11:50 +01:00
Rob Bradford
48faf3abac net_util, virtio-devices, vhost_user_net: Relocate {Rx,Tx}Virtio
By moving the code for opening the two RX and TX queues into a shared
location we are starting to remove the requirement for the
vhost-user-net backend to depend on the virtio-devices crate which in of
itself depends on many other crates that are not necessary for the
backend to function.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-08 16:11:50 +01:00
Rob Bradford
2efd307c4e net_util: Use two two different enums for MQ error
Rather than use an embedded String inside the MultiQueueSupport error
value use two different values to differentiate the two cases.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-08 16:11:50 +01:00
Rob Bradford
3e807a19b7 net_util, virtio-devices, vhost_user_net: Relocate code for opening TAP
By moving the code for opening the TAP device into a shared location we
are starting to remove the requirement for the vhost-user-net backend to
depend on the virtio-devices crate which in of itself depends on many
other crates that are not necessary for the backend to function.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-08 16:11:50 +01:00
dependabot-preview[bot]
bb390981a9 build(deps): bump kvm-ioctls from d094064 to 37953e9
Bumps [kvm-ioctls](https://github.com/cloud-hypervisor/kvm-ioctls) from `d094064` to `37953e9`.
- [Release notes](https://github.com/cloud-hypervisor/kvm-ioctls/releases)
- [Commits](d094064fef...37953e968b)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-08 12:53:00 +00:00
Wei Liu
5bfac796b3 build: add a default feature KVM
It gets bubbled all the way up from hypervsior crate to top-level
Cargo.toml.

Cloud Hypervisor can't function without KVM at this point, so make it
a default feature.

Fix all scripts that use --no-default-features.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-08 11:07:15 +01:00
dependabot-preview[bot]
861337cc6f build(deps): bump libc from 0.2.71 to 0.2.72
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.71 to 0.2.72.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.71...0.2.72)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-08 05:11:25 +00:00
Sebastien Boeuf
ef554ba13c ci: Increase time to stabilize memory measurement
Check if increasing the time after the VM is spawned help with getting
more stable numbers for the base PSS.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-07 19:36:12 +02:00
Hui Zhu
b59f231092 tests: Add integration test to test_memory_hotplug for virtio-balloon
Signed-off-by: Hui Zhu <teawater@antfin.com>
2020-07-07 17:25:13 +01:00
Hui Zhu
800220acbb virtio-balloon: Store the balloon size to support reboot
This commit store balloon size to MemoryConfig.
After reboot, virtio-balloon can use this size to inflate back to
the size before reboot.

Signed-off-by: Hui Zhu <teawater@antfin.com>
2020-07-07 17:25:13 +01:00
Sebastien Boeuf
50da100afd virtio-devices: Fix virtio-balloon device
The implementation of the virtio-balloon was slightly wrong as it was
generating the GPA (Guest Physical Address) from the PFN (Page Frame
Number) which was a u32. That means the GPA was created as a u32, and
later a cast was done to extend it to a u64 type. Unfortunately, by
doing so, the GPA was wrong if the value was supposedly more than 32
bits.

That's why the PFN is casted into a u64 before the GPA is generated,
which creates the GPA on 64 bits directly.

Additionally, this patch simplifies the process_queue() function,
relying on multiple vm-memory helpers.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-07 17:25:13 +01:00
Hui Zhu
8ffbc3d031 vmm: api: ch-remote: Add balloon to VmResizeData
Signed-off-by: Hui Zhu <teawater@antfin.com>
2020-07-07 17:25:13 +01:00
Hui Zhu
f729b25a10 openapi: Add MemoryConfig balloon
Add MemoryConfig balloon to vmm/src/api/openapi/cloud-hypervisor.yaml.

Signed-off-by: Hui Zhu <teawater@antfin.com>
2020-07-07 17:25:13 +01:00
Hui Zhu
8b6b97b86f vmm: Add virtio-balloon support
This commit adds new option balloon to memory config.
Set it to on will open the balloon function.

Signed-off-by: Hui Zhu <teawater@antfin.com>
2020-07-07 17:25:13 +01:00
Hui Zhu
0d72aa782f virtio-devices: Add virtio-balloon device
Add virtio-balloon device.
It only supports the basic functions of the virtio-balloon.

Signed-off-by: Hui Zhu <teawater@antfin.com>
2020-07-07 17:25:13 +01:00
dependabot-preview[bot]
ccc276a222 build(deps): bump backtrace from 0.3.49 to 0.3.50
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.49 to 0.3.50.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.49...0.3.50)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-07 14:40:07 +00:00
Henry Wang
603445e0a5 tests: Add a Jenkins stage for AArch64 integration test
This commit adds a Jenkins stage for AArch64 integration test,
and the test is carried out using the GNU toolchain.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-07-07 13:55:39 +01:00
Henry Wang
33c5d6bb98 tests: Enable test_aarch64_pe_boot case for AArch64
The compiled AArch64 linux kernel by running `make` is in PE format
instead of vmlinux, vmlinux.pvh and bzImage format. Therefore we
need to add integration test for this case.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-07-07 13:55:39 +01:00
Henry Wang
2ce231370d tests: Preparation of enabling integration tests for AArch64
This commit adds supporting components and code for enabling the
AArch64 integration tests, including:

1. A Linux kernel config file to build kernel on AArch64 machines.

2. Refactoring the `run_integration_test.sh` to architecture
specific scripts for readability.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-07-07 13:55:39 +01:00
Rob Bradford
b69f6d4f6c vhost_user_net, vhost_user_block, option_parser: Remove vmm dependency
Remove the vmm dependency from vhost_user_block and vhost_user_net where
it was existing to use config::OptionParser. By moving the OptionParser
to its own crate at the top-level we can remove the very heavy
dependency that these vhost-user backends had.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-06 18:33:29 +01:00
dependabot-preview[bot]
90261bb9c7 build(deps): bump smallvec from 1.4.0 to 1.4.1
Bumps [smallvec](https://github.com/servo/rust-smallvec) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/compare/v1.4.0...v1.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-06 16:38:18 +00:00
Michael Zhao
726e45e0ce vmm: Divide Seccomp KVM IOCTL rules by architecture
Refactored the construction of KVM IOCTL rules for Seccomp.
Separating the rules by architecture can reduce the risk of bugs and
attacks.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-06 13:40:38 +01:00
Wei Liu
a4f484bc5e hypervisor: Define a VM-Exit abstraction
In order to move the hypervisor specific parts of the VM exit handling
path, we're defining a generic, hypervisor agnostic VM exit enum.

This is what the hypervisor's Vcpu run() call should return when the VM
exit can not be completely handled through the hypervisor specific bits.
For KVM based hypervisors, this means directly forwarding the IO related
exits back to the VMM itself. For other hypervisors that e.g. rely on the
VMM to decode and emulate instructions, this means the decoding itself
would happen in the hypervisor crate exclusively, and the rest of the VM
exit handling would be handled through the VMM device model implementation.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

Fix test_vm unit test by using the new abstraction and dropping some
dead code.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-06 12:59:43 +01:00
Wei Liu
cfa758fbb1 vmm, hypervisor: introduce and use make_user_memory_region
This removes the last KVM-ism from memory_manager. Also make use of that
method in other places.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-06 12:31:19 +02:00
Wei Liu
8d97d628c3 vmm: drop "kvm" from memory slot code
The code is purely for maintaining an internal counter. It is not really
tied to KVM.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-06 12:31:19 +02:00
Rob Bradford
f19f7490c6 build: Bulk upgrade dependencies
There are several dependencies that need updating so update them
manually rather than relying on dependabot. This will reduce the load on
the CI.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-06 10:46:17 +01:00
Rob Bradford
72802b34cd vhost_user_block: Move binary into vhost_user_block crate
The binary is still built in the same location but the source code and
the dependencies for it come from the vhost_user_block crate itself.

The binary will be built with:

`cargo build --all --bin vhost_user_block` or just `cargo build --all`

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-06 10:56:10 +02:00
Rob Bradford
6959d27e8c vhost_user_net: Move binary into vhost_user_net crate
The binary is still built in the same location but the source code and
the dependencies for it come from the vhost_user_net crate itself.

The binary will be built with:

`cargo build --all --bin vhost_user_net` or just `cargo build --all`

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-06 10:56:10 +02:00
Rob Bradford
7548283feb build: Remove unused dependencies from top-level Cargo.toml
With vhost_user_fs binary moved to its own crate the dependencies in the
top level can be trimmed significantly.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-06 10:56:10 +02:00
Rob Bradford
d6a05ceabb vhost_user_fs: Move binary into vhost_user_fs crate
The binary is still built in the same location but the source code and
the dependencies for it come from the vhost_user_fs crate itself.

The binary will be built with:

`cargo build --all --bin vhost_user_fs` or just `cargo build --all`

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-06 10:56:10 +02:00
Rob Bradford
b8be6bab16 tests: Prepare for splitting binaries by building all binaries
In preparation for splitting the binaries into their own crates start
building all the binaries in the workspace as part of the integration
testing suite.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-06 10:56:10 +02:00
Rob Bradford
6f59cdbb55 build: dev_cli: Prepare for splitting binaries by building all binaries
In preparation for splitting the binaries into their own crates start
building all the binaries in the workspace when running the build
command inside the container.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-06 10:56:10 +02:00
Rob Bradford
1e9956c8cb build: github: Prepare for splitting binaries by building all binaries
In preparation for splitting the binaries into their own crates start
building all the binaries in the workspace when doing a build as part of
the GitHub actions.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-06 10:56:10 +02:00
dependabot-preview[bot]
863ea97213 build(deps): bump vfio-ioctls from f6f81ee to 4c56812
Bumps [vfio-ioctls](https://github.com/cloud-hypervisor/vfio-ioctls) from `f6f81ee` to `4c56812`.
- [Release notes](https://github.com/cloud-hypervisor/vfio-ioctls/releases)
- [Commits](f6f81ee865...4c568127fe)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-06 08:45:57 +00:00
Samuel Ortiz
8186a8eee6 vmm: interrupt: Rename vm_fd
The _fd suffix is KVM specific. But since it now point to an hypervisor
agnostic hypervisor::Vm implementation, we should just rename it vm.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-07-06 09:35:30 +01:00
Samuel Ortiz
4cc8853fe4 vmm: device_manager: Rename vm_fd
The _fd suffix is KVM specific. But since it now point to an hypervisor
agnostic hypervisor::Vm implementation, we should just rename it vm.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-07-06 09:35:30 +01:00
Samuel Ortiz
2012287611 vmm: memory_manager: Rename fd variable into something more meaningful
The fd naming is quite KVM specific. Since we're now using the
hypervisor crate abstractions, we can rename those into something more
readable and meaningful.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-07-06 09:35:30 +01:00
Samuel Ortiz
acfe5eb94f vmm: vm: Rename fd variable into something more meaningful
The fd naming is quite KVM specific. Since we're now using the
hypervisor crate abstractions, we can rename those into something more
readable and meaningful.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-07-06 09:35:30 +01:00
Samuel Ortiz
3db4c003a3 vmm: cpu: Rename fd variable into something more meaningful
The fd naming is quite KVM specific. Since we're now using the
hypervisor crate abstractions, we can rename those into something more
readable and meaningful. Like e.g. vcpu or vm.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-07-06 09:35:30 +01:00
Samuel Ortiz
618722cdca hypervisor: cpu: Rename state getter and setter
vcpu.{set_}cpu_state() is a stutter.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-07-06 09:35:30 +01:00
Sebastien Boeuf
a3342bdb25 docs: Replace every mention of ClearLinux with Ubuntu
Now that our CI has transitioned from ClearLinux to Ubuntu images
exclusively, let's update the documentation to refer to Ubuntu images
instead of ClearLinux's ones.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-03 14:37:09 +02:00
Sebastien Boeuf
b452e8be00 docs: Update custom image instructions
Now that the project relies on Ubuntu images, the documentation related
to the creation of a custom image has been updated. It's important to
note this procedure could be applied to any other distribution, given
that the package manager's commands would be slightly different.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-03 14:37:09 +02:00
Sebastien Boeuf
bb9174601a ci: Separate parallel vs sequential tests
In order to differentiate tests that can be run in parallel versus
tests that must be run on their own, we move all tests into dedicated
modules.

The point is to avoid glitches in results that can be caused by the fact
that other tests (hence VMs) are running at the same time.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-03 13:08:37 +01:00
Rob Bradford
2a6eb31d5b vm-virtio, virtio-devices: Split device implementation from virt queues
Split the generic virtio code (queues and device type) from the
VirtioDevice trait, transport and device implementations.

This also simplifies the feature handling in vhost_user_backend as the
vm-virtio crate is no longer has any features.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-02 17:09:28 +01:00
dependabot-preview[bot]
9a628edfcf build(deps): bump cc from 1.0.56 to 1.0.57
Bumps [cc](https://github.com/alexcrichton/cc-rs) from 1.0.56 to 1.0.57.
- [Release notes](https://github.com/alexcrichton/cc-rs/releases)
- [Commits](https://github.com/alexcrichton/cc-rs/compare/1.0.56...1.0.57)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-02 14:40:59 +00:00
Wei Liu
44be3af9c8 arch: drop one level of indirection for boot_msr_entries
This makes setup_msrs hypervisor agnostic.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-07-02 13:47:58 +02:00
Sebastien Boeuf
c2920d2b6b ci: Move from Clear Linux to Ubuntu Focal Fossa cloud image
Move the CI to rely entirely on Ubuntu cloud images. It's worth noting
that both QCOW2 and RAW images from Ubuntu Focal Fossa have been
modified to include the tools needed from integration tests.
This means fio, iperf, iperf3, netcat and socat have been added to the
image. The snapd package have been fully removed as it was expecting the
support for squashfs (not present when using our own kernel from direct
kernel boot), which was causing some failures, and was preventing
cloud-init from terminating properly.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-02 13:47:16 +02:00
Sebastien Boeuf
1f9103bb67 test_data: Update Ubuntu cloud-init to support all integration tests
Updating the cloud-init files related to Ubuntu images is needed to be
able to use Ubuntu images for running all integration tests.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-07-02 13:47:16 +02:00
Rob Bradford
135ee4568e tests: Enhance rate_limited_copy() to also consider free disk space
With QCOW disk images the space needed is greater than the size of the
iamge as any "zero" blocks in the image are allocated when they are
touched making the image bigger.

Here we add a threshold of 6GiB with added debugging messages to ensure
that there is sufficient disk space to run the tests.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-02 11:52:49 +02:00
Michael Zhao
8820e9e133 vmm: Fix Seccomp filter for AArch64
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
2020-07-02 08:46:24 +01:00
Rob Bradford
8e43f886e1 build: Bump kvm-ioctls dependency after rebase
ch branch is now rebased on latest upstream master

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-07-01 18:46:05 +02:00
Henry Wang
b70e254700 resources: Dockerfile: Update Dockerfile for AArch64
This update of Dockerfile will add support to the AArch64
integration tests and musl building, including:

1. Installation of the missing `setcap` binary on AArch64.
On AArch64, `setcap` binary should be installed via `libcap2-bin`.
This binary is needed in the integration test.

2. Related support for the cloud-hypervisor binary building by
musl toolchain.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-07-01 09:51:15 +01:00
Wei Liu
92a62d80e9 README: update hypervisor-fw release
The CI is using 0.2.8.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 21:02:13 +01:00
Rob Bradford
f8a8a5ca68 arch: x86_64: Remove residual debugging from smbios code
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-30 15:50:33 +01:00
Rob Bradford
602f76e4fc arch: x86_64: Use symbolic constant for end of table
The other types use a symbolic constant so do the same for consistency.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-30 15:50:33 +01:00
Sebastien Boeuf
514978b54a ci: Improve test_snapshot_restore with multiple vCPUs
Extend the existing integration test test_snapshot_restore by testing
with more than one vCPU.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-30 14:03:03 +01:00
Sebastien Boeuf
e35d4c5b28 hypervisor: Store all supported MSRs
On x86 architecture, we need to save a list of MSRs as part of the vCPU
state. By providing the full list of MSRs supported by KVM, this patch
fixes the remaining snapshot/restore issues, as the vCPU is restored
with all its previous states.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-30 14:03:03 +01:00
Sebastien Boeuf
49b4fba283 hypervisor: Retrieve list of supported MSRs
Add a new function to the hypervisor trait so that the caller can
retrieve the list of MSRs supported by this hypervisor.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-30 14:03:03 +01:00
Sebastien Boeuf
e2b5c78dc5 hypervisor: Re-order vCPU state for storing and restoring
Some vCPU states such as MP_STATE can be modified while retrieving
other states. For this reason, it's important to follow a specific
order that will ensure a state won't be modified after it has been
saved. Comments about ordering requirements have been copied over
from Firecracker commit 57f4c7ca14a31c5536f188cacb669d2cad32b9ca.

This patch also set the previously saved VCPU_EVENTS, as this was
missing from the restore codepath.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-30 14:03:03 +01:00
Wei Liu
2b8accf49a vmm: interrupt: put KVM code into a kvm module
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
c31e747005 vmm: interrupt: generify impl InterruptManager for MsiInterruptManager
The logic can be shared among hypervisor implementations.

The 'static bound is used such that we don't need to deal with extra
lifetime parameter everywhere. It should be okay because we know the
entry type E doesn't contain any reference.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
ade904e356 vmm: interrupt: generify impl InterruptSourceGroup for MsiInterruptGroup
At this point we can use the same logic for all hypervisor
implementations.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
2b466ed80c vmm: interrupt: provide MsiInterruptGroupOps trait
Currently it only contains a function named set_gsi_routes.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
b2abead65b vmm: interrupt: provide and use extension trait RoutingEntryExt
This trait contains a function which produces a interrupt routing entry.

Implement that trait for KvmRoutingEntry and rewrite the update
function.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
4dbca81b86 vmm: interrupt: rename set_kvm_gsi_routes to set_gsi_routes
This function will be used to commit routing information to the
hypervisor.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
fd7b42e54d vmm: interrupt: inline mask_kvm_entry
The logic for looking up the correct interrupt can be shared among
hypervisors.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
0ec39da90c vmm: interrupt: generify KvmMsiInterruptManager
The observation is only the route entry is hypervisor dependent.

Keep a definition of KvmMsiInterruptManager to avoid too much code
churn.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
d5149e95cb vmm: interrupt: generify KvmRoutingEntry and KvmMsiInterruptGroup
The observation is that only the route field is hypervisor specific.

Provide a new function in blanket implementation. Also redefine
KvmRoutingEntry with RoutingEntry to avoid code churn.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
637f58bcd9 vmm: interrupt: drop Kvm prefix from KvmLegacyUserspaceInterruptManager
This data structure doesn't contain KVM specific stuff.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
Wei Liu
574cab6990 vmm: interrupt: create GSI hashmap directly
The observation is that the GSI hashmap remains untouched before getting
passed into the MSI interrupt manager. We can create that hashmap
directly in the interrupt manager's new function.

The drops one import from the interrupt module.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-30 12:09:42 +01:00
dependabot-preview[bot]
f3c8f827cc build(deps): bump linux-loader from 2a62f21 to ec930d7
Bumps [linux-loader](https://github.com/rust-vmm/linux-loader) from `2a62f21` to `ec930d7`.
- [Release notes](https://github.com/rust-vmm/linux-loader/releases)
- [Commits](2a62f21b44...ec930d700f)

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-30 07:05:06 +00:00
Rob Bradford
fbbe348447 arch: x86-64: Add missing End of Table entry
The OVMF firmware loops around looking for an entry marking the end of
the table. Without this entry processing the tables is an infinite loop.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-30 08:15:48 +02:00
Rob Bradford
2c3c335de6 arch: x86_64: Add basic SMBIOS support
Taken from crosvm: 44336b913126d73f9f8d6854f57aac92b5db809e and adapted
for Cloud Hypervisor.

This is basic and incomplete support but Linux correctly finds the DMI
data based on this:

root@clr-c6ed47bc1c9d473d9a3a8bddc50ee4cb ~ # dmesg | grep -i dmi
[    0.000000] DMI: Cloud Hypervisor cloud-hypervisor, BIOS 0

root@clr-c6ed47bc1c9d473d9a3a8bddc50ee4cb ~ # dmesg | grep -i smbio
[    0.000000] SMBIOS 3.2.0 present.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-30 08:15:48 +02:00
Wei Liu
24c051c663 vmm: hypervisor: drop duplicate comment
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-29 21:51:59 +01:00
Wei Liu
2518b9e3cd vmm: hypervisor: fix white space issues
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-29 21:51:59 +01:00
dependabot-preview[bot]
5ac3299234 build(deps): bump serde_json from 1.0.55 to 1.0.56
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.55 to 1.0.56.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.55...v1.0.56)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-29 19:54:47 +00:00
dependabot-preview[bot]
58980067f4 build(deps): bump ssh2 from 0.8.1 to 0.8.2
Bumps [ssh2](https://github.com/alexcrichton/ssh2-rs) from 0.8.1 to 0.8.2.
- [Release notes](https://github.com/alexcrichton/ssh2-rs/releases)
- [Commits](https://github.com/alexcrichton/ssh2-rs/compare/0.8.1...0.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-29 18:02:38 +00:00
dependabot-preview[bot]
d2780a6575 build(deps): bump unicode-width from 0.1.7 to 0.1.8
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.7 to 0.1.8.
- [Release notes](https://github.com/unicode-rs/unicode-width/releases)
- [Commits](https://github.com/unicode-rs/unicode-width/commits/v0.1.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-29 17:37:40 +00:00
Samuel Ortiz
5a6b8d6323 dev_cli: Add a shell command
And drop the caller into a privileged root shell.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-06-29 18:17:39 +01:00
Henry Wang
d824d55562 unit tests: Fix broken AArch64 unit tests
https://github.com/cloud-hypervisor/cloud-hypervisor/pull/1225
introduces a hypervisor abstraction crate, which breaks some of
the unit test cases on AArch64. This commit fixes related test
cases.

Signed-off-by: Henry Wang <henry.wang@arm.com>
2020-06-29 18:00:42 +01:00
Henry Wang
462c58d58b tests: Enable AArch64 Jenkins CI with unit tests for GNU
This commit enables the AArch64 Jenkins CI with build and running
unit tests for GNU toolchain.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-06-29 18:00:42 +01:00
dependabot-preview[bot]
ba3f1bcde2 build(deps): bump cc from 1.0.55 to 1.0.56
Bumps [cc](https://github.com/alexcrichton/cc-rs) from 1.0.55 to 1.0.56.
- [Release notes](https://github.com/alexcrichton/cc-rs/releases)
- [Commits](https://github.com/alexcrichton/cc-rs/compare/1.0.55...1.0.56)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-29 16:20:33 +00:00
Rob Bradford
6ee9903601 docs: Update API documentation
Update the API documentation to reflect that the hotplug APIs return
data about the device as well as the newly added /vm.counters API.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-27 00:07:47 +02:00
Rob Bradford
522d8c8412 vmm: openapi: Add the /vm.counters API entry point
This is a hash table of string to hash tables of u64s. In JSON these
hash tables are object types.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-27 00:07:47 +02:00
Muminul Islam
72ae1577ed hypervisor: Update license to Apache-2.0 OR BSD-3-Clause
Initially the licensing was just Apache-2.0. This patch changes
the licensing to dual license Apache-2.0 OR BSD-3-Clause

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-06-27 00:06:39 +02:00
dependabot-preview[bot]
e1ea06e74a build(deps): bump cc from 1.0.54 to 1.0.55
Bumps [cc](https://github.com/alexcrichton/cc-rs) from 1.0.54 to 1.0.55.
- [Release notes](https://github.com/alexcrichton/cc-rs/releases)
- [Commits](https://github.com/alexcrichton/cc-rs/compare/1.0.54...1.0.55)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-26 04:55:46 +00:00
dependabot-preview[bot]
19ce0a37a4 build(deps): bump epoll from 4.3.0 to 4.3.1
Bumps [epoll](https://github.com/nathansizemore/epoll) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/nathansizemore/epoll/releases)
- [Commits](https://github.com/nathansizemore/epoll/commits/4.3.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-25 14:03:04 +00:00
Sebastien Boeuf
a7f0f9dfea vm-virtio: Ensure pause event is caught by every virtio thread
Each virtio thread was reading/draining the pause_evt pipe when
detecting the associated event. Problem is, when a virtio device has
multiple threads, they all share the same pause_evt pipe, which can
prevent some threads from receiving the event. If the first thread to
catch the event is quickly clearing the pipe, some other threads might
simply miss the event and they will not enter the "paused" state as
expected.

This is a behavior that was spotted with virtio-net as it usually uses
2 threads by default (1 for TX/RX queues and 1 for the control queue).

The way to solve this issue is by letting each thread drain the pipe
during the resume codepath, that is after the thread has been unparked.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-25 12:01:34 +02:00
Sebastien Boeuf
86377127df vmm: Resume devices after vCPUs have been resumed
Because we don't want the guest to miss any event triggered by the
emulation of devices, it is important to resume all vCPUs before we can
resume the DeviceManager with all its associated devices.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-25 12:01:34 +02:00
Sebastien Boeuf
f6eeba781b vmm: Save and restore vCPU states during pause/resume operations
We need consistency between pause/resume and snapshot/restore
operations. The symmetrical behavior of pausing/snapshotting
and restoring/resuming has been introduced recently, and we must
now ensure that no matter if we're using pause/resume or
snapshot/restore features, the resulting VM should be running in
the exact same way.

That's why the vCPU state is now stored upon VM pausing. The snapshot
operation being a simple serialization of the previously saved state.
The same way, the vCPU state is now restored upon VM resuming. The
restore operation being a simple deserialization of the previously
restored state.

It's interesting to note that this patch ensures time consistency from a
guest perspective, no matter which clocksource is being used. From a
previous patch, the KVM clock was saved/restored upon VM pause/resume.
We now have the same behavior for TSC, as the TSC from the vCPUs are
saved/restored upon VM pause/resume too.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-25 12:01:34 +02:00
Sebastien Boeuf
18e7d7a1f7 vmm: cpu: Resume before shutdown in a specific way
Instead of calling the resume() function from the CpuManager, which
involves more than what is needed from the shutdown codepath, and
potentially ends up with a deadlock, we replace it with a subset.

The full resume operation is reserved for a VM that has been paused.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-25 12:01:34 +02:00
Sebastien Boeuf
65132fb99d vmm: Implement Pausable trait for Vcpu
We want each Vcpu to store the vCPU state upon VM pausing. This is the
reason why we need to explicitly implement the Pausable trait for the
Vcpu structure.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-25 12:01:34 +02:00
Wei Liu
1741af74ed hypervisor: add safety statement in set_user_memory_region
When set_user_memory_region was moved to hypervisor crate, it was turned
into a safe function that wrapped around an unsafe call. All but one
call site had the safety statements removed. But safety statement was
not moved inside the wrapper function.

Add the safety statement back to help reasoning in the future. Also
remove that one last instance where the safety statement is not needed .

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-25 10:25:13 +02:00
Wei Liu
b27439b6ed arch, hypervisor, vmm: KvmHyperVisor -> KvmHypervisor
"Hypervisor" is one word. The "v" shouldn't be capitalised.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-25 10:25:13 +02:00
Wei Liu
b00171e17d vmm: use MemoryRegion where applicable
That removes one more KVM-ism in VMM crate.

Note that there are more KVM specific code in those files to be split
out, but we're not at that stage yet.

No functional change.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-25 10:25:13 +02:00
Rob Bradford
48a05c4727 tests: Add simple counters integration test
Add a simple test to check that the data from the counters matches what
is expected and that the value of the counters increases after an
operation that will hit all counters.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Rob Bradford
980b49da94 vm-virtio: block: Implement counters for block device
Expose counters for read/write bytes/ops from the virtio block device.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Rob Bradford
d983c0a680 vmm: Expose counters from virtio devices to API
Collate the virtio device counters in DeviceManager for each device that
exposes any and expose it through the recently added HTTP API.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Rob Bradford
9b7afd4aac bin: ch-remote: Implement "counters" command
This is used to obtain the counters from the VM. The raw JSON data is
presented to the user.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Rob Bradford
bca8a19244 vmm: Implement HTTP API for obtaining counters
The counters are a hash of device name to hash of counter name to u64
value. Currently the API is only implemented with a stub that returns an
empty set of counters.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Rob Bradford
fd4aba8eae vmm: api: Implement support for GET handlers EndpointHandler
This can be used for simple API requests which return data but do not
require any input.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Rob Bradford
80be393b16 vmm: api: Order HTTP entry points in alphabetical order
Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Rob Bradford
6713a3c859 vm-virtio: net: Expose network counters through VirtioDevice
Through the counters() function on the trait expose the accumulated
counters.

TEST=Observe that the counters from the VM match those from the tap on
the host (RX-TX inverted) and inside the guest (non inverted.)

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Rob Bradford
dd54883a07 vm-virtio: device: Extend the VirtioDevice trait to expose counters
The counters are a hash of counter name to (wrapping) u64 value. The
interpretation layer is responsible for converting this data into a
rate.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Rob Bradford
2b4a0358de vm-virtio: net: Implement counters for network traffic
Add counters for RX/TX bytes and RX/TX frames. These are collected on a
per queue basis and then accumulated into an atomic shared value across
the different threads for the device as a whole.

Collecting and accumulating these counters makes minimal difference in
the iperf results. Any difference seen is within what is observed as
natural variation in this test.

e.g.

With counter updates:

$ iperf3 -c 192.168.249.2
Connecting to host 192.168.249.2, port 5201
[  5] local 192.168.249.1 port 52706 connected to 192.168.249.2 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  6.19 GBytes  53.2 Gbits/sec    0   3.01 MBytes
[  5]   1.00-2.00   sec  6.31 GBytes  54.2 Gbits/sec    0   3.01 MBytes
[  5]   2.00-3.00   sec  6.29 GBytes  54.0 Gbits/sec    0   3.01 MBytes
[  5]   3.00-4.00   sec  6.22 GBytes  53.4 Gbits/sec    0   3.01 MBytes
[  5]   4.00-5.00   sec  6.14 GBytes  52.8 Gbits/sec    0   3.01 MBytes
[  5]   5.00-6.00   sec  6.13 GBytes  52.7 Gbits/sec    0   3.01 MBytes
[  5]   6.00-7.00   sec  6.20 GBytes  53.3 Gbits/sec    0   3.01 MBytes
[  5]   7.00-8.00   sec  6.16 GBytes  52.9 Gbits/sec    0   3.01 MBytes
[  5]   8.00-9.00   sec  6.13 GBytes  52.6 Gbits/sec    0   3.01 MBytes
[  5]   9.00-10.00  sec  6.15 GBytes  52.8 Gbits/sec    0   3.01 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  61.9 GBytes  53.2 Gbits/sec    0             sender
[  5]   0.00-10.00  sec  61.9 GBytes  53.2 Gbits/sec                  receiver

iperf Done.

Without counter updates:

$ iperf3 -c 192.168.249.2
Connecting to host 192.168.249.2, port 5201
[  5] local 192.168.249.1 port 52716 connected to 192.168.249.2 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  6.29 GBytes  54.1 Gbits/sec    0   3.03 MBytes
[  5]   1.00-2.00   sec  6.18 GBytes  53.1 Gbits/sec    0   3.03 MBytes
[  5]   2.00-3.00   sec  6.26 GBytes  53.8 Gbits/sec    0   3.03 MBytes
[  5]   3.00-4.00   sec  6.24 GBytes  53.6 Gbits/sec    0   3.03 MBytes
[  5]   4.00-5.00   sec  6.27 GBytes  53.9 Gbits/sec    1   3.03 MBytes
[  5]   5.00-6.00   sec  6.31 GBytes  54.2 Gbits/sec    0   3.03 MBytes
[  5]   6.00-7.00   sec  6.29 GBytes  54.1 Gbits/sec    0   3.03 MBytes
[  5]   7.00-8.00   sec  6.16 GBytes  52.9 Gbits/sec    0   3.03 MBytes
[  5]   8.00-9.00   sec  6.13 GBytes  52.6 Gbits/sec    0   3.03 MBytes
[  5]   9.00-10.00  sec  6.25 GBytes  53.7 Gbits/sec    0   3.03 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  62.4 GBytes  53.6 Gbits/sec    1             sender
[  5]   0.00-10.00  sec  62.4 GBytes  53.6 Gbits/sec                  receiver

iperf Done.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-25 07:02:44 +02:00
Dr. David Alan Gilbert
0583ce921b vhost_user_fs: Allow fchmod in seccomp
This corresponds to QEMU's 63659fe74e76f5c52854 commit.
the setattr code uses both fchmod and fchmodat in different cases,
however we only had fchmodat in the whitelist.

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
2020-06-24 21:56:58 +01:00
Wei Liu
4cc37d7b9a vmm: interrupt: drop a few pub keywords
Those items are not used elsewhere. Restrict their scope.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-24 12:39:42 +02:00
Wei Liu
1661adbbaf vmm: interrupt: add "Kvm" prefix to MsiInterruptGroup
The structure is tightly coupled with KVM. It uses KVM specific
structures and calls. Add Kvm prefix to it.

Microsoft hypervisor will implement its own interrupt group(s) later.

No functional change intended.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-24 12:39:42 +02:00
Sebastien Boeuf
9f4714c32a vmm: Extend seccomp filters with KVM_KVMCLOCK_CTRL
Now that the VMM uses KVM_KVMCLOCK_CTRL from the KVM API, it must be
added to the seccomp filters list.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-24 12:38:56 +02:00
Sebastien Boeuf
4a81d65f79 vmm: Notify the guest about vCPUs being paused
Through the newly added API notify_guest_clock_paused(), this patch
improves the vCPU pause operation by letting the guest know that each
vCPU is being paused. This is important to avoid soft lockups detection
from the guest that could happen because the VM has been paused for more
than 20 seconds.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-24 12:38:56 +02:00
Sebastien Boeuf
309924eca8 hypervisor: Add guest pause notification
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-24 12:38:56 +02:00
Sebastien Boeuf
9fa8438063 vmm: Fill CpuManager's vCPU list on restore path
It's important that on restore path, the CpuManager's vCPU gets filled
with each new vCPU that is being created. In order to cover both boot
and restore paths, the list is being filled from the common function
create_vcpu().

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-24 12:38:56 +02:00
Sebastien Boeuf
0b45f6364e build(deps): Update to latest kvm-ioctls
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-24 12:38:56 +02:00
dependabot-preview[bot]
7f955ff122 build(deps): bump epoll from 4.2.0 to 4.3.0
Bumps [epoll](https://github.com/nathansizemore/epoll) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/nathansizemore/epoll/releases)
- [Commits](https://github.com/nathansizemore/epoll/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-24 05:39:26 +00:00
Sebastien Boeuf
f5150aa261 vmm: Extend seccomp filters with KVM_GET_CLOCK and KVM_SET_CLOCK
Now that the VMM uses both KVM_GET_CLOCK and KVM_SET_CLOCK from the KVM
API, they must be added to the seccomp filters list.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-23 14:36:01 +01:00
Sebastien Boeuf
8038161861 vmm: Get and set clock during pause and resume operations
In order to maintain correct time when doing pause/resume and
snapshot/restore operations, this patch stores the clock value
on pause, and restore it on resume. Because snapshot/restore
expects a VM to be paused before the snapshot and paused after
the restore, this covers the migration use case too.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-23 14:36:01 +01:00
Sebastien Boeuf
68ec0eb7a8 hypervisor: Add get/set clock API
Add the hypervisor wrappers to expose KVM_GET_CLOCK and KVM_SET_CLOCK
from the KVM API.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-23 14:36:01 +01:00
Rob Bradford
4b64f2a027 vmm: cpu: Reuse already allocated vCPUs if available
When a request is made to increase the number of vCPUs in the VM attempt
to reuse any previously removed (and hence inactive) vCPUs before
creating new ones.

This ensures that the APIC ID is not reused for a different KVM vCPU
(which is not allowed) and that the APIC IDs are also sequential.

The two key changes to support this are:

* Clearing the "kill" bit on the old vCPU state so that it does not
  immediately exit upon thread recreation.
* Using the length of the vcpus vector (the number of allocated vcpus)
  rather than the number of active vCPUs (.present_vcpus()) to determine
  how many should be created.

This change also introduced some new info!() debugging on the vCPU
creation/removal path to aid further development in the future.

TEST=Expanded test_cpu_hotplug test.

Fixes: #1338

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-23 14:11:14 +01:00
Rob Bradford
9dcd0c37f3 vmm: cpu: Clear the "kill" flag on vCPU to support reuse
After the vCPU has been ejected and the thread shutdown it is useful to
clear the "kill" flag so that if the vCPU is reused it does not
immediately exit upon thread recreation.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-23 14:11:14 +01:00
Rob Bradford
b107bfcf2c vmm: cpu: Add info!() level debugging to vCPU handling
These messages are intended to be useful to support debugging related to
vCPU hotplug/unplug issues.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-23 14:11:14 +01:00
dependabot-preview[bot]
3c2bfbdc95 build(deps): bump aho-corasick from 0.7.12 to 0.7.13
Bumps [aho-corasick](https://github.com/BurntSushi/aho-corasick) from 0.7.12 to 0.7.13.
- [Release notes](https://github.com/BurntSushi/aho-corasick/releases)
- [Commits](https://github.com/BurntSushi/aho-corasick/compare/0.7.12...0.7.13)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-23 13:03:45 +00:00
Sebastien Boeuf
b5cf150ac3 ci: Update test_snapshot_restore by resuming the VM
Now that snapshot/restore is symmetrical, that is the VM must be paused
before it is snapshot and it must be resumed after it's been restored,
the integration test is updated accordingly.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-23 10:15:03 +02:00
Sebastien Boeuf
e382dc6657 vmm, vm-virtio: Restore DeviceManager's devices in a paused state
The same way the VM and the vCPUs are restored in a paused state, all
devices associated with the device manager must be restored in the same
paused state.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-23 10:15:03 +02:00
Sebastien Boeuf
8a165b5314 vmm: Restore the VM in "paused" state
Because we need to pause the VM before it is snapshot, it should be
restored in a paused state to keep the sequence symmetrical. That's the
reason why the state machine regarding the valid VM's state transition
needed to be updated accordingly.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-23 10:15:03 +02:00
Sebastien Boeuf
a16414dc87 vmm: Restore vCPUs in "paused" state
To follow a symmetrical model, and avoid potential race conditions, it's
important to restore a previously snapshot VM in a "paused" state.

The snapshot operation being valid only if the VM has been previously
paused.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-23 10:15:03 +02:00
Wei Liu
7552f4db61 vmm: device_manager: restore error handling
When the hypervisor crate was introduced, a few places that handled
errors were commented out in favor of unwrap, but that's bad practice.

Restore proper error handling in those places in this patch.

We cannot use from_raw_os_error anymore because it is wrapped deep under
hypervisor crate. Create new custom errors instead.

Fixes: e4dee57e81 ("arch, pci, vmm: Initial switch to the hypervisor crate")
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-22 22:02:21 +01:00
Muminul Islam
cca59bc52f hypervisor, arch: Fix warnings introduced in hypervisor crate
This commit fixes some warnings introduced in the previous
hyperviosr crate PR.Removed some unused variables from arch/aarch64
module.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-06-22 21:58:45 +01:00
dependabot-preview[bot]
4ce7af08d8 build(deps): bump aho-corasick from 0.7.10 to 0.7.12
Bumps [aho-corasick](https://github.com/BurntSushi/aho-corasick) from 0.7.10 to 0.7.12.
- [Release notes](https://github.com/BurntSushi/aho-corasick/releases)
- [Commits](https://github.com/BurntSushi/aho-corasick/compare/0.7.10...0.7.12)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 21:57:15 +01:00
Rob Bradford
d714efe6d4 vmm: cpu: Import CpuTopology conditionally on x86_64 only
The aarch64 build has no use for this structure at the moment.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-22 15:00:27 +01:00
Sebastien Boeuf
a998e89375 build(deps): bump signal-hook from 0.1.15 to 0.1.16
Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.1.15
to 0.1.16.
- [Release notes](https://github.com/vorner/signal-hook/releases)
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](vorner/signal-hook@v0.1.15...v0.1.16)

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-22 14:09:11 +01:00
Muminul Islam
e4dee57e81 arch, pci, vmm: Initial switch to the hypervisor crate
Start moving the vmm, arch and pci crates to being hypervisor agnostic
by using the hypervisor trait and abstractions. This is not a complete
switch and there are still some remaining KVM dependencies.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-06-22 15:03:15 +02:00
Muminul Islam
c48d0c1a67 hypervisor: kvm: Implement vCPU state method
Implement the vCPU state getter and setter separately from the initial
KVM Hypervisor trait implementation, mostly for readability purposes.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-06-22 15:03:15 +02:00
Muminul Islam
72e39a3496 hypervisor: Add KVM implementation
For each of the traits we are defining kvm related structures
and add the trait implementation to the structs. For more information
please see the kvm-ioctls and kvm-bindings crate.

This is a standalone implementation that does not include the switch of
the Cloud-Hypervisor vmm and arch crates to it.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-06-22 15:03:15 +02:00
Muminul Islam
683210d6e9 hypervisor: Add Hypervisor trait
As the only hypervisor that Cloud-Hypervisor supports is KVM, the
Hypervisor trait accomodates for the upcoming KVM implementation.

This trait will be instanciated at build time through hypervisor
specific features, i.e. it's not aiming at run-time selection of
hypervisors for Cloud-Hypervisor.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-06-22 15:03:15 +02:00
Muminul Islam
f5afc288d8 hypervisor: Add Vm trait
This VM trait should be implemented by each underlying hypervisor.

Previously created hypervisor object should create the VM based on
already selected hypervisor. This is just the trait definition. For each
of supported hypervisor we need to implement the trait. Later we will
implement this trait for KVM and then Microsoft Hyper-V.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-06-22 15:03:15 +02:00
Muminul Islam
f9b51a41b5 hypervisor: Add Vcpu trait
This Vcpu trait should be implemented by each underlying hypervisor.

Previously created hypervisor object should create the VM based on
already selected hypervisor and Vm object should create this vcpu
object based on same hyperviosr. Each of this object should be
referenced by trait object i.e <dyn Vcpu>.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2020-06-22 15:03:15 +02:00
Muminul Islam
56a1638517 hypervisor: Introduce hypervisor crate
The purpose of this trait is to add support for other hypervisors than
KVM, like e.g. Microsoft Hyper-V.

Further commits will define additional hypervisor related traits like
Vcpu and Vm. Each of the supported hypervisor will need to implement all
traits defined from the hypervisor crate.

Signed-off-by: Muminul Islam <muislam@microsoft.com>
2020-06-22 15:03:15 +02:00
dependabot-preview[bot]
b969d92fe3 build(deps): bump serde from 1.0.113 to 1.0.114
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.113 to 1.0.114.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.113...v1.0.114)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 06:03:59 +00:00
dependabot-preview[bot]
30fb6732a2 build(deps): bump serde_derive from 1.0.113 to 1.0.114
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.113 to 1.0.114.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.113...v1.0.114)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 05:18:53 +00:00
dependabot-preview[bot]
22fc3bfaaf build(deps): bump syn from 1.0.32 to 1.0.33
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.32 to 1.0.33.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.32...1.0.33)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 05:18:24 +00:00
dependabot-preview[bot]
c0d42dc1bf build(deps): bump syn from 1.0.31 to 1.0.32
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.31 to 1.0.32.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.31...1.0.32)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-21 18:09:04 +00:00
dependabot-preview[bot]
3a102c6573 build(deps): bump addr2line from 0.12.1 to 0.12.2
Bumps [addr2line](https://github.com/gimli-rs/addr2line) from 0.12.1 to 0.12.2.
- [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.12.1...0.12.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-21 18:08:44 +00:00
dependabot-preview[bot]
62eb66d87e build(deps): bump serde from 1.0.112 to 1.0.113
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.112 to 1.0.113.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.112...v1.0.113)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-20 13:36:11 +01:00
dependabot-preview[bot]
7e8c1ade12 build(deps): bump serde_derive from 1.0.112 to 1.0.113
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.112 to 1.0.113.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.112...v1.0.113)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-20 10:58:57 +01:00
dependabot-preview[bot]
025941ba73 build(deps): bump epoll from 4.1.0 to 4.2.0
Bumps [epoll](https://github.com/nathansizemore/epoll) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/nathansizemore/epoll/releases)
- [Commits](https://github.com/nathansizemore/epoll/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-19 06:02:13 +00:00
Rob Bradford
ec246b9702 build: Enable LTO for release build
This results in ~20% reduction of stripped binary size:

Default release build: 8.7M -> stripped: 5.3M
LTO release build: 5.9M -> stripped: 4.3M

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-17 21:44:21 +01:00
Rob Bradford
798f3f01a9 tests: Add integration tests for CPU topology
Add multiple integration tests with various different CPU topologies and
check that they work as expected.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-17 12:18:09 +02:00
Rob Bradford
a74c6fc14f vmm, arch: x86_64: Fill the CPUID leaves with the topology
There are two CPUID leaves for handling CPU topology, 0xb and 0x1f. The
difference between the two is that the 0x1f leaf (Extended Topology
Leaf) supports exposing multiple die packages.

Fixes: #1284

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-17 12:18:09 +02:00
Rob Bradford
e19079782d vmm, arch: x86_64: Set the APIC ID on the 0x1f CPUID leaf
The extended topology leaf (0x1f) also needs to have the APIC ID (which
is the KVM cpu ID) set. This mirrors the APIC ID set on the 0xb topology
leaf

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-17 12:18:09 +02:00
Rob Bradford
b81bc77390 vmm: cpu: Save CpusConfig into CpuManager
Rather than saving the individual parts into the CpuManager save the
full struct as it now also contains the topology data.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-17 12:18:09 +02:00
Rob Bradford
284891b5df main: Populate "--cpus" with appropriate syntax definition
This needed to be updated to include specifying the boot and maxmium
vCPUs as well as the newly added topology for those vCPUs.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-17 12:18:09 +02:00
Rob Bradford
4a0439a993 vmm: config: Extend CpusConfig to add the topology
This allows the user to optionally specify the desired CPU topology. All
parts of the topology must be specified and the product of all parts
must match the maximum vCPUs.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-17 12:18:09 +02:00
dependabot-preview[bot]
9099b067e1 build(deps): bump linux-loader from f7f2e89 to 2a62f21
Bumps [linux-loader](https://github.com/rust-vmm/linux-loader) from `f7f2e89` to `2a62f21`.
- [Release notes](https://github.com/rust-vmm/linux-loader/releases)
- [Commits](f7f2e890f7...2a62f21b44)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-17 09:34:08 +02:00
dependabot-preview[bot]
1d27416fd1 build(deps): bump unicode-normalization from 0.1.12 to 0.1.13
Bumps [unicode-normalization](https://github.com/unicode-rs/unicode-normalization) from 0.1.12 to 0.1.13.
- [Release notes](https://github.com/unicode-rs/unicode-normalization/releases)
- [Commits](https://github.com/unicode-rs/unicode-normalization/compare/v0.1.12...v0.1.13)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-16 22:49:54 +02:00
Stefano Garzarella
f756174b9f vsock: add muxer rxq regression test
This patch has been cherry-picked from the Firecracker tree. The
reference commit is 78ca0a942f32140465c67ea4b45d68c52c72d751.

Signed-off-by: Gabriel Ionescu <gbi@amazon.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-16 22:02:06 +02:00
Stefano Garzarella
bb3cf7c30c vsock: add handshake regression test
This patch has been cherry-picked from the Firecracker tree. The
reference commit is 6dbe8e021a64ba3742081741a7538cdfd93a102e.

Signed-off-by: Gabriel Ionescu <gbi@amazon.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-16 22:02:06 +02:00
Stefano Garzarella
6ab4e247e6 vsock: fixed TX buf flushing
This patch has been cherry-picked from the Firecracker tree. The
reference commit is 78819f35f63f5777a58e3e1e774b3270b32881ed.

The vsock TX buffer flush operation would report inconsistent results,
under specific circumstances.

The flush operation is performed in two steps, since it's dealing with a
ring buffer, an the data to be flushed may wrap around. If the first
step was successful, but the second one failed, the whole flush
operation would report an error, thus causing flow control accounting to
lose track of the bytes that were successfully written by the first
pass.

This commit changes the flush behavior to always report success when
some data has been written to the backing stream.

Signed-off-by: Dan Horobeanu <dhr@amazon.com>
Signed-off-by: Gabriel Ionescu <gbi@amazon.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-16 22:02:06 +02:00
Stefano Garzarella
aca2baf458 vsock: fixed flow control regression
This patch has been cherry-picked from the Firecracker tree. The
reference commit is 2da612a9cdce85c91fb54ab22d950ec6ccc93b27.

Fixed a bug introduced by a271d08f0b1ba0ee82761cd49244b6a8017bcede,
whereby the flow control accouting would be off by a few bytes, for
host-initiated connections.

The connection ack message ("OK <port_num><CR>") was accounted for as
data sent by the guest, so its length was substracted from the total
amount of data the guest was allowed to send.

This commit changes the way this ack message is sent, so that it
bypasses flow control accouting.

Signed-off-by: Dan Horobeanu <dhr@amazon.com>
Signed-off-by: Gabriel Ionescu <gbi@amazon.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-16 22:02:06 +02:00
Stefano Garzarella
3ce4bd5ec8 vsock: absorb spurious EPOLLOUT events
This patch has been cherry-picked from the Firecracker tree. The
reference commit is 109e631566350867dafa4b16c3919dfd1533eeea.

This commit changes the vsock connection state machine behavior to absorb
any EWOULDBLOCK errors recevied while handling an EPOLLOUT event. Previously,
this condition would lead to immediate connection termination.

Signed-off-by: Dan Horobeanu <dhr@amazon.com>
Signed-off-by: Gabriel Ionescu <gbi@amazon.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-16 22:02:06 +02:00
Stefano Garzarella
0530b4e1ed vsock: absorb spurious EPOLLIN events
This patch has been cherry-picked from the Firecracker tree. The
reference commit is 660d18cf7fee5b38c3b1b17a5da6544b9025909d.

Apparently, epoll_wait sometimes yields false EPOLLIN events (i.e. events
follwing which read() would fail with EWOULDBLOCK). This would cause the
vsock connection state machine to terminate connections, since an error
was detected on the underlying Unix socket.

This commit changes the vsock connection state machine code to handle such
erroneous EPOLLIN events by absorbing EWOULDBLOCK read() errors.

Signed-off-by: Dan Horobeanu <dhr@amazon.com>
Signed-off-by: Gabriel Ionescu <gbi@amazon.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-16 22:02:06 +02:00
Stefano Garzarella
a3f24e5fb9 vsock: flow control fix
This patch has been cherry-picked from the Firecracker tree. The
reference commit is 1cc8b8a678eb28b20f5843556bdb7fbb2dfa6284.

Fixed a logical error in the vsock flow control, that would cause credit
update packets to not be sent at the right time.

Signed-off-by: Dan Horobeanu <dhr@amazon.com>
Signed-off-by: Gabriel Ionescu <gbi@amazon.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-16 22:02:06 +02:00
Stefano Garzarella
5fc52a5056 vsock: fixed rxq logic
This patch has been cherry-picked from the Firecracker tree. The
reference commit is d2475773557c82d2abad2fc8bdf69e7d01444109.

Fixed a vsock muxer issue that would cause a connection to be removed
from the RX queue, even though it still had pending RX data.

Signed-off-by: Dan Horobeanu <dhr@amazon.com>
Signed-off-by: Gabriel Ionescu <gbi@amazon.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-16 22:02:06 +02:00
dependabot-preview[bot]
69dac2dbb5 build(deps): bump remain from 0.2.1 to 0.2.2
Bumps [remain](https://github.com/dtolnay/remain) from 0.2.1 to 0.2.2.
- [Release notes](https://github.com/dtolnay/remain/releases)
- [Commits](https://github.com/dtolnay/remain/compare/0.2.1...0.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-16 21:53:01 +02:00
dependabot-preview[bot]
19ab08f239 build(deps): bump itoa from 0.4.5 to 0.4.6
Bumps [itoa](https://github.com/dtolnay/itoa) from 0.4.5 to 0.4.6.
- [Release notes](https://github.com/dtolnay/itoa/releases)
- [Commits](https://github.com/dtolnay/itoa/compare/0.4.5...0.4.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-16 21:52:43 +02:00
dependabot-preview[bot]
a17aaec663 build(deps): bump adler32 from 1.0.4 to 1.1.0
Bumps [adler32](https://github.com/remram44/adler32-rs) from 1.0.4 to 1.1.0.
- [Release notes](https://github.com/remram44/adler32-rs/releases)
- [Commits](https://github.com/remram44/adler32-rs/compare/1.0.4...1.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-16 08:44:15 +02:00
dependabot-preview[bot]
8e5c816c2b build(deps): bump thiserror from 1.0.19 to 1.0.20
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.19 to 1.0.20.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.19...1.0.20)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-16 08:16:52 +02:00
Stefano Garzarella
096ffe08f2 vm-virtio: vsock: add is_empty method to VsockPacket
This patch adds `is_empty` method to VsockPacket to fix the
following clippy error:

error: item `vsock::packet::VsockPacket` has a public `len` method but no corresponding `is_empty` method
   --> vm-virtio/src/vsock/packet.rs:100:1
    |
100 | / impl VsockPacket {
101 | |     /// Create the packet wrapper from a TX virtq chain head.
102 | |     ///
103 | |     /// The chain head is expected to hold valid packet header data. A following packet buffer
...   |
334 | |     }
335 | | }
    | |_^
    |
    = note: `-D clippy::len-without-is-empty` implied by `-D warnings`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty

Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-15 18:31:54 +01:00
Stefano Garzarella
b74a855446 vm-virtio: make VsockPacket public
This patch makes VsockPacket public to allow other crates
(e.g. vhost-user-vsock) to use it.

Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
2020-06-15 18:31:54 +01:00
dependabot-preview[bot]
8bcd0abbe5 build(deps): bump backtrace from 0.3.48 to 0.3.49
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.48 to 0.3.49.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.48...0.3.49)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-15 17:31:53 +01:00
Henry Wang
99e72be169 unit tests: Fix unit tests and docs for AArch64
Currently, not every feature of the cloud-hypervisor is enabled
on AArch64, which means that on AArch64 machines, the
`run_unit_tests.sh` needs to be tailored and some unit test cases
should be run on x86_64 only.

Also this commit fixes the typo and unifies `Arm64` and `AArch64`
in the AArch64 document.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
2020-06-15 17:28:05 +01:00
Wei Liu
103cd61bd2 vmm: device_tree: make available remove function unconditionally
Its test case calls remove unconditionally. Instead of making the test
code call remove conditionally, removing the pci_support dependency
simplifies things -- that function is just a wrapper around HashMap's
remove function anyway.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-15 11:41:34 +02:00
dependabot-preview[bot]
66f7d20ee5 build(deps): bump linux-loader from 1af92d2 to f7f2e89
Bumps [linux-loader](https://github.com/rust-vmm/linux-loader) from `1af92d2` to `f7f2e89`.
- [Release notes](https://github.com/rust-vmm/linux-loader/releases)
- [Commits](1af92d21ad...f7f2e890f7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-15 08:56:24 +02:00
LiYa'nan
acc234088f vfio: fix for bug as below:
cloud-hypervisor: 763.978581807s: ERROR:pci/src/vfio.rs:651 -- failed to remove all guest memory regions from iommu table

when poweroff a vm with vfio device, clh will finally remove all guest memory region from iommu table
with the method unset_dma_map, not method setup_dma_map.

Signed-off-by: LiYa'nan <oliverliyn@gmail.com>
2020-06-15 08:51:13 +02:00
Rob Bradford
e080c6e5b0 Revert "build: Temporarily disable nightly builds"
This reverts commit 1a2b3c8f3e.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-15 06:43:27 +02:00
dependabot-preview[bot]
36f6d9da52 build(deps): bump serde from 1.0.111 to 1.0.112
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.111 to 1.0.112.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.111...v1.0.112)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-14 20:26:25 +01:00
dependabot-preview[bot]
9a99ebfd81 build(deps): bump serde_derive from 1.0.111 to 1.0.112
Bumps [serde_derive](https://github.com/serde-rs/serde) from 1.0.111 to 1.0.112.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.111...v1.0.112)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-14 20:26:01 +01:00
dependabot-preview[bot]
cb67372de2 build(deps): bump proc-macro-nested from 0.1.5 to 0.1.6
Bumps [proc-macro-nested](https://github.com/dtolnay/proc-macro-hack) from 0.1.5 to 0.1.6.
- [Release notes](https://github.com/dtolnay/proc-macro-hack/releases)
- [Commits](https://github.com/dtolnay/proc-macro-hack/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-14 13:02:33 +01:00
dependabot-preview[bot]
4171155540 build(deps): bump hermit-abi from 0.1.13 to 0.1.14
Bumps [hermit-abi](https://github.com/hermitcore/libhermit-rs) from 0.1.13 to 0.1.14.
- [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>
2020-06-14 13:02:25 +01:00
dependabot-preview[bot]
2072145d71 build(deps): bump pin-project from 0.4.20 to 0.4.22
Bumps [pin-project](https://github.com/taiki-e/pin-project) from 0.4.20 to 0.4.22.
- [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/v0.4.20...v0.4.22)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-14 13:02:14 +01:00
dependabot-preview[bot]
9eaa735ccb build(deps): bump remove_dir_all from 0.5.2 to 0.5.3
Bumps [remove_dir_all](https://github.com/XAMPPRocky/remove_dir_all) from 0.5.2 to 0.5.3.
- [Release notes](https://github.com/XAMPPRocky/remove_dir_all/releases)
- [Changelog](https://github.com/XAMPPRocky/remove_dir_all/blob/master/CHANGELOG.md)
- [Commits](https://github.com/XAMPPRocky/remove_dir_all/compare/v0.5.2...remove_dir_all@0.5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-12 18:56:34 +01:00
Wei Liu
fb461c820f vmm: vm: enable test_vm test case
Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-12 14:46:58 +01:00
Wei Liu
b99b5777bb vmm: vm: move some imports into test_vm
They are only needed there. Not moving them causes rustc to complain
about unused imports.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-12 14:46:58 +01:00
Sebastien Boeuf
4f311b56eb ci: Extend integration tests for checking PCI hotplug response
Upon PCI hotplug, the VMM now returns some information about the device
name and the associated b/d/f. This patch extends the integration tests
so that we validate the response is the one that is expected.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-12 13:37:18 +01:00
Sebastien Boeuf
b62d5d22ff vmm: openapi: Update the OpenAPI definition
Now that PCI device hotplug returns a response, the OpenAPI definition
must reflect it, describing what is expected to be received.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-12 13:37:18 +01:00
Sebastien Boeuf
4fe7347fb9 vmm: Manually implement Serialize for PciDeviceInfo
In order to provide a more comprehensive b/d/f to the user, the
serialization of PciDeviceInfo is implemented manually to control the
formatting.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-12 13:37:18 +01:00
Sebastien Boeuf
83cd9969df vmm: Enable HTTP response for PCI device hotplug
This patch completes the series by connecting the dots between the HTTP
frontend and the device manager backend.

Any request to hotplug a VFIO, disk, fs, pmem, net, or vsock device will
now return a response including the device name and the place of the
device in the PCI topology.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-12 13:37:18 +01:00
Sebastien Boeuf
3316348d4c vmm: vm: Carry information from hotplugged PCI device
Pass from the device manager to the calling code the information about
the PCI device that has just been hotplugged.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-12 13:37:18 +01:00
Sebastien Boeuf
f08e9b6a73 vmm: device_manager: Return PciDeviceInfo from a hotplugged device
In order to provide the device name and PCI b/d/f associated with a
freshly hotplugged device, the hotplugging functions from the device
manager return a new structure called PciDeviceInfo.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-12 13:37:18 +01:00
Sebastien Boeuf
0bc2b08d3a vmm: api: Return an optional response from vm_action()
Any action that relies on vm_action() can now return a response body.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-12 13:37:18 +01:00
Sebastien Boeuf
038180269e vmm: api: Allow HTTP PUT request to return a response
Adding the codepath to return a response from a PUT request.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2020-06-12 13:37:18 +01:00
Rob Bradford
1a2b3c8f3e build: Temporarily disable nightly builds
Rust nightly builds are currently failing due to a crate outside of
our control not meeting the latest cargo requirements.

See XAMPPRocky/remove_dir_all#19

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-06-12 10:07:00 +01:00
Wei Liu
5ebd02a572 vmm: vm: fix test_vm test case
We should break out from the loop after getting the HLT exit, otherwise
the VM hangs forever.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
2020-06-12 08:38:07 +02:00
169 changed files with 18622 additions and 8651 deletions

View File

@@ -26,4 +26,4 @@ jobs:
override: true
- name: Build
run: cargo build --release --target=${{ matrix.target }}
run: cargo build --all --release --target=${{ matrix.target }}

View File

@@ -24,11 +24,9 @@ jobs:
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: Disable "with-serde" in kvm-bindings
run: sed -i 's/"with-serde",\ //g' vmm/Cargo.toml
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: true
command: build
args: --target=${{ matrix.target }} --no-default-features --features "mmio"
args: --target=${{ matrix.target }} --no-default-features --features "mmio,kvm"

View File

@@ -20,9 +20,9 @@ jobs:
toolchain: stable
target: x86_64-unknown-linux-musl
- name: Build
run: cargo build --release --target=x86_64-unknown-linux-gnu
run: cargo build --all --release --target=x86_64-unknown-linux-gnu
- name: Static Build
run: cargo build --release --target=x86_64-unknown-linux-musl
run: cargo build --all --release --target=x86_64-unknown-linux-musl
- name: Strip cloud-hypervisor binaries
run: strip target/*/release/cloud-hypervisor
- name: Create Release

404
Cargo.lock generated
View File

@@ -9,18 +9,24 @@ dependencies = [
[[package]]
name = "addr2line"
version = "0.12.1"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543"
checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
dependencies = [
"gimli",
]
[[package]]
name = "aho-corasick"
version = "0.7.10"
name = "adler"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "aho-corasick"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
dependencies = [
"memchr",
]
@@ -31,14 +37,14 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
name = "anyhow"
version = "1.0.31"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
[[package]]
name = "arc-swap"
@@ -51,12 +57,13 @@ name = "arch"
version = "0.1.0"
dependencies = [
"acpi_tables",
"anyhow",
"arch_gen",
"byteorder",
"kvm-bindings",
"kvm-ioctls",
"hypervisor",
"libc",
"linux-loader",
"log 0.4.11",
"rand 0.7.3",
"vm-memory",
]
@@ -85,7 +92,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@@ -96,13 +103,14 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "backtrace"
version = "0.3.48"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130"
checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
@@ -136,6 +144,22 @@ dependencies = [
"constant_time_eq",
]
[[package]]
name = "block_util"
version = "0.1.0"
dependencies = [
"io-uring",
"libc",
"log 0.4.11",
"serde",
"serde_derive",
"serde_json",
"virtio-bindings",
"vm-memory",
"vm-virtio",
"vmm-sys-util",
]
[[package]]
name = "byteorder"
version = "1.3.4"
@@ -144,9 +168,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "cc"
version = "1.0.54"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
[[package]]
name = "cfg-if"
@@ -156,9 +180,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "clap"
version = "2.33.1"
version = "2.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
checksum = "10040cdf04294b565d9e0319955430099ec3813a64c952b86a41200ad714ae48"
dependencies = [
"ansi_term",
"atty",
@@ -172,32 +196,23 @@ dependencies = [
[[package]]
name = "cloud-hypervisor"
version = "0.8.0"
version = "0.9.0"
dependencies = [
"arc-swap",
"clap",
"credibility",
"dirs",
"epoll",
"futures",
"hypervisor",
"lazy_static",
"libc",
"log 0.4.8",
"log 0.4.11",
"net_util",
"seccomp",
"serde_json",
"ssh2",
"tempdir",
"tempfile",
"vhost",
"vhost_user_backend",
"vhost_user_block",
"vhost_user_fs",
"vhost_user_net",
"virtio-bindings",
"vm-device",
"vm-memory",
"vm-virtio",
"vmm",
"vmm-sys-util",
]
@@ -248,7 +263,7 @@ dependencies = [
"byteorder",
"epoll",
"libc",
"log 0.4.8",
"log 0.4.11",
"serde",
"serde_derive",
"serde_json",
@@ -261,11 +276,10 @@ dependencies = [
[[package]]
name = "dirs"
version = "2.0.2"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff"
dependencies = [
"cfg-if",
"dirs-sys",
]
@@ -277,14 +291,14 @@ checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
dependencies = [
"libc",
"redox_users",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
name = "epoll"
version = "4.1.0"
version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990bcfe26bea89669ede68c3f970f61d02568dbc8660317c98d805ea4e710685"
checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0"
dependencies = [
"bitflags 1.2.1",
"libc",
@@ -427,9 +441,9 @@ dependencies = [
[[package]]
name = "gimli"
version = "0.21.0"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]]
name = "glob"
@@ -439,13 +453,31 @@ checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
[[package]]
name = "hermit-abi"
version = "0.1.13"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
dependencies = [
"libc",
]
[[package]]
name = "hypervisor"
version = "0.1.0"
dependencies = [
"anyhow",
"kvm-bindings",
"kvm-ioctls",
"libc",
"linux-loader",
"log 0.4.11",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"vm-memory",
"vmm-sys-util",
]
[[package]]
name = "idna"
version = "0.2.0"
@@ -457,6 +489,15 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "io-uring"
version = "0.3.5"
source = "git+https://github.com/tokio-rs/io-uring.git?branch=0.4#c00d968b038263a02a72d6510edaf438b0c7b4f3"
dependencies = [
"bitflags 1.2.1",
"libc",
]
[[package]]
name = "ipnetwork"
version = "0.16.0"
@@ -468,9 +509,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "kernel32-sys"
@@ -485,7 +526,7 @@ dependencies = [
[[package]]
name = "kvm-bindings"
version = "0.2.0"
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch#3a6780089e0e2d69aaf77666524e81801c814bdd"
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch#6453253aff8b804796bce547c7a23e09464b7491"
dependencies = [
"serde",
"serde_derive",
@@ -495,7 +536,7 @@ dependencies = [
[[package]]
name = "kvm-ioctls"
version = "0.5.0"
source = "git+https://github.com/cloud-hypervisor/kvm-ioctls?branch=ch#5b7f1cc5de6d9664ee30e7d6a2f183499de0f685"
source = "git+https://github.com/cloud-hypervisor/kvm-ioctls?branch=ch#37953e968b9064f42d6b71b7afc13872ac4171ce"
dependencies = [
"kvm-bindings",
"libc",
@@ -510,15 +551,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.71"
version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10"
[[package]]
name = "libssh2-sys"
version = "0.2.17"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d45f516b9b19ea6c940b9f36d36734062a153a2b4cc9ef31d82c54bb9780f525"
checksum = "eafa907407504b0e683786d4aba47acf250f114d37357d56608333fd167dd0fc"
dependencies = [
"cc",
"libc",
@@ -543,7 +584,7 @@ dependencies = [
[[package]]
name = "linux-loader"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/linux-loader#1af92d21adfab105e05146625d7dace58892612d"
source = "git+https://github.com/rust-vmm/linux-loader#b309c9df171bb6c3eba361382c54e82716c6824c"
dependencies = [
"vm-memory",
]
@@ -563,14 +604,14 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
dependencies = [
"log 0.4.8",
"log 0.4.11",
]
[[package]]
name = "log"
version = "0.4.8"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
]
@@ -590,11 +631,20 @@ checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "micro_http"
version = "0.1.0"
source = "git+https://github.com/firecracker-microvm/micro-http#c9ffb90aebdc77a8fade2e4196f311f9bbfcce82"
source = "git+https://github.com/firecracker-microvm/micro-http#40309b72ee084b0a26d60208f0872bd7c986de45"
dependencies = [
"epoll",
]
[[package]]
name = "miniz_oxide"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f"
dependencies = [
"adler",
]
[[package]]
name = "net_gen"
version = "0.1.0"
@@ -606,13 +656,18 @@ dependencies = [
name = "net_util"
version = "0.1.0"
dependencies = [
"epoll",
"lazy_static",
"libc",
"log 0.4.11",
"net_gen",
"pnet",
"rand 0.7.3",
"serde",
"serde_json",
"virtio-bindings",
"vm-memory",
"vm-virtio",
"vmm-sys-util",
]
@@ -628,9 +683,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.19.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2"
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
[[package]]
name = "once_cell"
@@ -651,6 +706,10 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "option_parser"
version = "0.1.0"
[[package]]
name = "parking_lot"
version = "0.10.2"
@@ -672,7 +731,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@@ -682,14 +741,13 @@ dependencies = [
"anyhow",
"byteorder",
"devices",
"kvm-bindings",
"kvm-ioctls",
"hypervisor",
"libc",
"log 0.4.8",
"log 0.4.11",
"serde",
"serde_derive",
"serde_json",
"vfio-bindings 0.2.0 (git+https://github.com/rust-vmm/vfio-bindings)",
"vfio-bindings",
"vfio-ioctls",
"vm-allocator",
"vm-device",
@@ -706,18 +764,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pin-project"
version = "0.4.20"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e75373ff9037d112bb19bc61333a06a159eaeb217660dcfbea7d88e1db823919"
checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "0.4.20"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10b4b44893d3c370407a1d6a5cfde7c41ae0478e31c516c85f67eb3adc51be6d"
checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f"
dependencies = [
"proc-macro2",
"quote",
@@ -732,9 +790,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.17"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
[[package]]
name = "pnet"
@@ -833,23 +891,23 @@ checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "proc-macro-hack"
version = "0.5.16"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
[[package]]
name = "proc-macro-nested"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0afe1bd463b9e9ed51d0e0f0b50b6b146aec855c56fd182bb242388710a9b6de"
checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
[[package]]
name = "proc-macro2"
version = "1.0.18"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
dependencies = [
"unicode-xid 0.2.0",
"unicode-xid 0.2.1",
]
[[package]]
@@ -858,10 +916,9 @@ version = "0.1.0"
dependencies = [
"byteorder",
"libc",
"log 0.4.8",
"log 0.4.11",
"remain",
"tempfile",
"vm-virtio",
"vmm-sys-util",
]
@@ -884,7 +941,7 @@ dependencies = [
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@@ -954,9 +1011,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.1.56"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_users"
@@ -989,9 +1046,9 @@ checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
[[package]]
name = "remain"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99c861227fc40c8da6fdaa3d58144ac84c0537080a43eb1d7d45c28f88dcb888"
checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
dependencies = [
"proc-macro2",
"quote",
@@ -1000,11 +1057,11 @@ dependencies = [
[[package]]
name = "remove_dir_all"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@@ -1046,22 +1103,22 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "seccomp"
version = "0.1.0"
source = "git+https://github.com/firecracker-microvm/firecracker?tag=v0.21.1#047a379eb041f9ceae35df5fa072d88cac55340e"
source = "git+https://github.com/firecracker-microvm/firecracker?tag=v0.22.0#cc5387637c132e500b4857b80b5a239d8d732b77"
dependencies = [
"libc",
]
[[package]]
name = "serde"
version = "1.0.111"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
[[package]]
name = "serde_derive"
version = "1.0.111"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
dependencies = [
"proc-macro2",
"quote",
@@ -1070,9 +1127,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.55"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226"
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
dependencies = [
"itoa",
"ryu",
@@ -1081,9 +1138,9 @@ dependencies = [
[[package]]
name = "signal-hook"
version = "0.1.15"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ff2db2112d6c761e12522c65f7768548bd6e8cd23d2a9dae162520626629bd6"
checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
dependencies = [
"libc",
"signal-hook-registry",
@@ -1091,9 +1148,9 @@ dependencies = [
[[package]]
name = "signal-hook-registry"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
dependencies = [
"arc-swap",
"libc",
@@ -1107,15 +1164,15 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f"
[[package]]
name = "ssh2"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd3183fb5dfacfd604fd33be6f581eb060a54d5fdc828d49b41de3c9309459a9"
checksum = "ba56d741dab9a295bcb131ebfbe57f8fea2e1b7ae203e9184f5d7648213e4460"
dependencies = [
"bitflags 1.2.1",
"libc",
@@ -1131,13 +1188,13 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.31"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid 0.2.0",
"unicode-xid 0.2.1",
]
[[package]]
@@ -1149,7 +1206,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid 0.2.0",
"unicode-xid 0.2.1",
]
[[package]]
@@ -1222,7 +1279,7 @@ dependencies = [
"rand 0.7.3",
"redox_syscall",
"remove_dir_all",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@@ -1242,7 +1299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
dependencies = [
"libc",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@@ -1257,18 +1314,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.19"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344"
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.19"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
dependencies = [
"proc-macro2",
"quote",
@@ -1284,6 +1341,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "tinyvec"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed"
[[package]]
name = "unicode-bidi"
version = "0.3.4"
@@ -1295,18 +1358,18 @@ dependencies = [
[[package]]
name = "unicode-normalization"
version = "0.1.12"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
dependencies = [
"smallvec",
"tinyvec",
]
[[package]]
name = "unicode-width"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
@@ -1316,9 +1379,9 @@ checksum = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb"
[[package]]
name = "unicode-xid"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "url"
@@ -1346,27 +1409,22 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "vfio-bindings"
version = "0.2.0"
source = "git+https://github.com/rust-vmm/vfio-bindings#f08cbcbf4041c981441d9c036c49ebad5098ed1c"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a21f546f2bda37f5a8cfb138c87f95b8e34d2d78d6a7a92ba3785f4e08604a7"
dependencies = [
"vmm-sys-util",
]
[[package]]
name = "vfio-bindings"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a21f546f2bda37f5a8cfb138c87f95b8e34d2d78d6a7a92ba3785f4e08604a7"
[[package]]
name = "vfio-ioctls"
version = "0.1.0"
source = "git+https://github.com/cloud-hypervisor/vfio-ioctls?branch=ch#f6f81ee865cb60a24ad5d4ba8200940ed1d5d1f4"
source = "git+https://github.com/cloud-hypervisor/vfio-ioctls?branch=ch#49cc3626f6787e2075493a511675c6851a13bbc0"
dependencies = [
"byteorder",
"kvm-bindings",
"kvm-ioctls",
"log 0.4.8",
"vfio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.11",
"vfio-bindings",
"vm-memory",
"vmm-sys-util",
]
@@ -1374,7 +1432,7 @@ dependencies = [
[[package]]
name = "vhost"
version = "0.1.0"
source = "git+https://github.com/cloud-hypervisor/vhost?branch=dragonball#3e80cf531d278aeb7f947335a4eeca655e0f2c54"
source = "git+https://github.com/cloud-hypervisor/vhost?branch=dragonball#422964150a69afd4611708076c60cb34c64a9856"
dependencies = [
"bitflags 1.2.1",
"libc",
@@ -1387,7 +1445,7 @@ version = "0.1.0"
dependencies = [
"epoll",
"libc",
"log 0.4.8",
"log 0.4.11",
"vhost",
"virtio-bindings",
"vm-memory",
@@ -1399,17 +1457,17 @@ dependencies = [
name = "vhost_user_block"
version = "0.1.0"
dependencies = [
"bitflags 1.2.1",
"block_util",
"clap",
"epoll",
"libc",
"log 0.4.8",
"log 0.4.11",
"option_parser",
"qcow",
"vhost",
"vhost_user_backend",
"virtio-bindings",
"vm-memory",
"vm-virtio",
"vmm",
"vmm-sys-util",
]
@@ -1418,30 +1476,36 @@ name = "vhost_user_fs"
version = "0.1.0"
dependencies = [
"bitflags 1.2.1",
"clap",
"epoll",
"futures",
"libc",
"log 0.4.8",
"log 0.4.11",
"seccomp",
"tempdir",
"vhost",
"vhost_user_backend",
"virtio-bindings",
"virtio-devices",
"vm-memory",
"vm-virtio",
"vmm-sys-util",
]
[[package]]
name = "vhost_user_net"
version = "0.1.0"
dependencies = [
"bitflags 1.2.1",
"clap",
"epoll",
"libc",
"log 0.4.8",
"log 0.4.11",
"net_util",
"option_parser",
"vhost",
"vhost_user_backend",
"virtio-bindings",
"vm-memory",
"vm-virtio",
"vmm",
"vmm-sys-util",
]
@@ -1451,6 +1515,38 @@ 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",
"devices",
"epoll",
"io-uring",
"libc",
"log 0.4.11",
"net_gen",
"net_util",
"pci",
"seccomp",
"serde",
"serde_derive",
"serde_json",
"tempfile",
"vfio-ioctls",
"vhost",
"virtio-bindings",
"vm-allocator",
"vm-device",
"vm-memory",
"vm-migration",
"vm-virtio",
"vmm-sys-util",
]
[[package]]
name = "vm-allocator"
version = "0.1.0"
@@ -1475,12 +1571,12 @@ dependencies = [
[[package]]
name = "vm-memory"
version = "0.2.1"
source = "git+https://github.com/cloud-hypervisor/vm-memory?branch=ch#708e9aa5d4317f7044d9835fe5080287e9c76f21"
version = "0.2.2"
source = "git+https://github.com/cloud-hypervisor/vm-memory?branch=ch#b28148d201b464acdef3124116ea43bb645cfd2f"
dependencies = [
"arc-swap",
"libc",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@@ -1498,28 +1594,12 @@ dependencies = [
name = "vm-virtio"
version = "0.1.0"
dependencies = [
"anyhow",
"arc-swap",
"byteorder",
"devices",
"epoll",
"libc",
"log 0.4.8",
"net_gen",
"net_util",
"pci",
"log 0.4.11",
"serde",
"serde_derive",
"serde_json",
"tempfile",
"vfio-ioctls",
"vhost",
"virtio-bindings",
"vm-allocator",
"vm-device",
"vm-memory",
"vm-migration",
"vmm-sys-util",
]
[[package]]
@@ -1530,18 +1610,19 @@ dependencies = [
"anyhow",
"arc-swap",
"arch",
"block_util",
"clap",
"credibility",
"devices",
"epoll",
"kvm-bindings",
"kvm-ioctls",
"hypervisor",
"lazy_static",
"libc",
"linux-loader",
"log 0.4.8",
"log 0.4.11",
"micro_http",
"net_util",
"option_parser",
"pci",
"qcow",
"seccomp",
@@ -1552,6 +1633,7 @@ dependencies = [
"tempfile",
"url",
"vfio-ioctls",
"virtio-devices",
"vm-allocator",
"vm-device",
"vm-memory",
@@ -1586,9 +1668,9 @@ checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",

View File

@@ -1,74 +1,73 @@
[package]
name = "cloud-hypervisor"
version = "0.8.0"
version = "0.9.0"
authors = ["The Cloud Hypervisor Authors"]
edition = "2018"
default-run = "cloud-hypervisor"
build = "build.rs"
[profile.release]
lto = true
[dependencies]
arc-swap = ">=0.4.4"
clap = { version = "2.33.1", features=["wrap_help"] }
epoll = ">=4.0.1"
futures = { version = "0.3.5", features = ["thread-pool"] }
lazy_static = "1.4.0"
libc = "0.2.71"
log = { version = "0.4.8", features = ["std"] }
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.21.1" }
serde_json = ">=1.0.9"
vhost_user_backend = { path = "vhost_user_backend"}
clap = { version = "2.33.2", features = ["wrap_help"] }
hypervisor = { path = "hypervisor" }
libc = "0.2.74"
log = { version = "0.4.11", features = ["std"] }
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.22.0" }
serde_json = "1.0.57"
vhost_user_block = { path = "vhost_user_block"}
vhost_user_fs = { path = "vhost_user_fs"}
vhost_user_net = { path = "vhost_user_net"}
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
vmm = { path = "vmm" }
vm-device = { path = "vm-device" }
vm-memory = "0.2.1"
vmm-sys-util = "0.6.1"
vm-virtio = { path = "vm-virtio" }
vhost_rs = { git = "https://github.com/cloud-hypervisor/vhost", branch = "dragonball", package = "vhost", features = ["vhost-user-slave"] }
[patch.crates-io]
vm-memory = { git = "https://github.com/cloud-hypervisor/vm-memory", branch = "ch" }
[dev-dependencies]
ssh2 = "0.8.1"
dirs = "2.0.2"
ssh2 = "0.8.2"
dirs = "3.0.1"
credibility = "0.1.3"
tempdir= "0.3.7"
tempdir = "0.3.7"
lazy_static= "1.4.0"
tempfile = "3.1.0"
serde_json = ">=1.0.9"
serde_json = "1.0.57"
net_util = { path = "net_util" }
[features]
default = ["acpi", "pci", "cmos"]
default = ["acpi", "pci", "cmos", "kvm"]
acpi = ["vmm/acpi"]
pci = ["vmm/pci_support"]
mmio = ["vmm/mmio_support"]
cmos = ["vmm/cmos"]
fwdebug = ["vmm/fwdebug"]
kvm = ["vmm/kvm"]
io_uring = ["vmm/io_uring"]
# Integration tests require a special environment to run in
integration_tests = []
[workspace]
members = [
"acpi_tables",
"arch",
"arch_gen",
"block_util",
"devices",
"qcow",
"hypervisor",
"net_gen",
"net_util",
"option_parser",
"pci",
"vmm",
"vm-virtio",
"vm-device",
"vm-migration",
"vhost_user_block",
"qcow",
"vhost_user_backend",
"vhost_user_block",
"vhost_user_fs",
"vhost_user_net",
"net_util",
"acpi_tables",
"arch_gen",
"net_gen",
"virtio-devices",
"vmm",
"vm-allocator",
"vm-device",
"vm-migration",
"vm-virtio"
]

24
Jenkinsfile vendored
View File

@@ -61,6 +61,9 @@ pipeline{
}
}
stage ('Run integration tests') {
options {
retry(3)
}
steps {
sh "scripts/dev_cli.sh tests --integration"
}
@@ -84,6 +87,9 @@ pipeline{
}
}
stage ('Run integration tests for musl') {
options {
retry(3)
}
steps {
sh "scripts/dev_cli.sh tests --integration --libc musl"
}
@@ -93,6 +99,22 @@ pipeline{
}
}
}
post {
regression {
script {
if (env.BRANCH_NAME == 'master') {
slackSend (color: '#ff0000', message: '"master" branch build is now failing')
}
}
}
fixed {
script {
if (env.BRANCH_NAME == 'master') {
slackSend (color: '#00ff00', message: '"master" branch build is now fixed')
}
}
}
}
}
def cancelPreviousBuilds() {
@@ -108,4 +130,4 @@ def cancelPreviousBuilds() {
build.doStop()
}
}
}
}

View File

@@ -19,6 +19,7 @@
4. [rust-vmm dependency](#4-rust-vmm-dependency)
* [Firecracker and crosvm](#firecracker-and-crosvm)
5. [Community](#5-community)
* [Contribute](#contribute)
* [Join us](#join-us)
6. [Security](#6-security)
@@ -127,13 +128,13 @@ Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) project
an ELF
formatted KVM firmware for `cloud-hypervisor` to directly boot into.
We need to get the latest `rust-hypervisor-firmware` release and also a working cloud image. Here we will use a Clear Linux image:
We need to get the latest `rust-hypervisor-firmware` release and also a working cloud image. Here we will use a Ubuntu image:
```shell
$ pushd $CLOUDH
$ wget https://download.clearlinux.org/releases/31890/clear/clear-31890-kvm.img.xz
$ unxz clear-31890-kvm.img.xz
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.2.6/hypervisor-fw
$ 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
$ popd
```
@@ -142,7 +143,7 @@ $ pushd $CLOUDH
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
--kernel ./hypervisor-fw \
--disk path=clear-31890-kvm.img \
--disk path=focal-server-cloudimg-amd64.raw \
--cpus boot=4 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
@@ -169,7 +170,7 @@ $ git clone --depth 1 https://github.com/cloud-hypervisor/linux.git -b virtio-fs
$ pushd linux-cloud-hypervisor
# Use the cloud-hypervisor kernel config to build your kernel
$ cp $CLOUDH/cloud-hypervisor/resources/linux-config .config
$ cp $CLOUDH/cloud-hypervisor/resources/linux-config-x86_64 .config
$ make bzImage -j `nproc`
$ popd
```
@@ -178,18 +179,18 @@ The `vmlinux` kernel image will then be located at `linux-cloud-hypervisor/arch/
#### Disk image
For the disk image, we will use a Clear Linux cloud image that contains a root partition:
For the disk image, we will use a Ubuntu cloud image that contains a root partition:
```shell
$ pushd $CLOUDH
$ wget https://download.clearlinux.org/releases/31890/clear/clear-31890-kvm.img.xz
$ unxz clear-31890-kvm.img.xz
$ 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
$ popd
```
#### Booting the guest VM
Now we can directly boot into our custom kernel and make it use the Clear Linux root partition.
Now we can directly boot into our custom kernel and make it use the Ubuntu root partition.
If we want to have 4 vCPUs and 512 MBytes of memory:
```shell
@@ -197,8 +198,8 @@ $ pushd $CLOUDH
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
--disk path=clear-31890-kvm.img \
--cmdline "console=hvc0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda3" \
--disk path=focal-server-cloudimg-amd64.raw \
--cmdline "console=hvc0 root=/dev/vda1 rw" \
--cpus boot=4 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
@@ -217,8 +218,8 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
--console off \
--serial tty \
--disk path=clear-31890-kvm.img \
--cmdline "console=ttyS0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda3" \
--disk path=focal-server-cloudimg-amd64.raw \
--cmdline "console=ttyS0 root=/dev/vda1 rw" \
--cpus boot=4 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
@@ -229,8 +230,7 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
`cloud-hypervisor` is in a very early, pre-alpha stage. Use at your own risk!
As of 2020-04-23, the following cloud images are supported:
* [Clear Linux](https://download.clearlinux.org/current/) (cloudguest and kvm)
As of 2020-07-02, the following cloud images are supported:
* [Ubuntu Bionic](https://cloud-images.ubuntu.com/bionic/current/) (cloudimg)
* [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (cloudimg)
@@ -287,6 +287,11 @@ crates as possible.
# 5. Community
The Cloud Hypervisor project follows the governance, and community guidelines described in
the [Community](https://github.com/cloud-hypervisor/community) repository.
## Contribute
We are working on building a global, diverse and collaborative community around the Cloud Hypervisor project.
Anyone who is interested in [contributing](CONTRIBUTING.md) to the project is welcome to participate.

View File

@@ -13,12 +13,12 @@ pub struct GenericAddress {
}
impl GenericAddress {
pub fn io_port_address(address: u16) -> Self {
pub fn io_port_address<T>(address: u16) -> Self {
GenericAddress {
address_space_id: 1,
register_bit_width: 8,
register_bit_width: 8 * std::mem::size_of::<T>() as u8,
register_bit_offset: 0,
access_size: 1,
access_size: std::mem::size_of::<T>() as u8,
address: u64::from(address),
}
}

View File

@@ -8,12 +8,12 @@ default = []
acpi = ["acpi_tables"]
[dependencies]
anyhow = "1.0"
byteorder = "1.3.4"
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch" }
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
libc = "0.2.71"
hypervisor = { path = "../hypervisor" }
libc = "0.2.74"
log = "0.4.11"
vm-memory = { version = "0.2.1", features = ["backend-mmap"] }
acpi_tables = { path = "../acpi_tables", optional = true }
arch_gen = { path = "../arch_gen" }

View File

@@ -17,14 +17,20 @@ use super::super::DeviceType;
use super::super::InitramfsConfig;
use super::get_fdt_addr;
use super::gic::GICDevice;
use super::layout::FDT_MAX_SIZE;
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 vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap};
// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
const GIC_PHANDLE: u32 = 1;
// This is a value for uniquely identifying the FDT node declaring the MSI controller.
const MSI_PHANDLE: u32 = 2;
// This is a value for uniquely identifying the FDT node containing the clock definition.
const CLOCK_PHANDLE: u32 = 2;
const CLOCK_PHANDLE: u32 = 3;
// Read the documentation specified when appending the root node to the FDT.
const ADDRESS_CELLS: u32 = 0x2;
const SIZE_CELLS: u32 = 0x2;
@@ -91,6 +97,7 @@ pub fn create_fdt<T: DeviceInfoForFDT + Clone + Debug>(
device_info: &HashMap<(DeviceType, String), T>,
gic_device: &Box<dyn GICDevice>,
initrd: &Option<InitramfsConfig>,
pci_space_address: &Option<(u64, u64)>,
) -> Result<Vec<u8>> {
// Alocate stuff necessary for the holding the blob.
let mut fdt = vec![0; FDT_MAX_SIZE];
@@ -119,6 +126,9 @@ pub fn create_fdt<T: DeviceInfoForFDT + Clone + Debug>(
create_clock_node(&mut fdt)?;
create_psci_node(&mut fdt)?;
create_devices_node(&mut fdt, device_info)?;
if let Some((pci_device_base, pci_device_size)) = pci_space_address {
create_pci_nodes(&mut fdt, *pci_device_base, *pci_device_size)?;
}
// End Header node.
append_end_node(&mut fdt)?;
@@ -389,6 +399,17 @@ fn create_gic_node(fdt: &mut Vec<u8>, gic_device: &Box<dyn GICDevice>) -> Result
let gic_intr_prop = generate_prop32(&gic_intr);
append_property(fdt, "interrupts", &gic_intr_prop)?;
if gic_device.msi_compatible() {
append_begin_node(fdt, "msic")?;
append_property_string(fdt, "compatible", gic_device.msi_compatiblility())?;
append_property_null(fdt, "msi-controller")?;
append_property_u32(fdt, "phandle", MSI_PHANDLE)?;
let msi_reg_prop = generate_prop64(gic_device.msi_properties());
append_property(fdt, "reg", &msi_reg_prop)?;
append_end_node(fdt)?;
}
append_end_node(fdt)?;
Ok(())
@@ -519,6 +540,10 @@ fn create_devices_node<T: DeviceInfoForFDT + Clone + Debug>(
// Sort out virtio devices by address from low to high and insert them into fdt table.
ordered_virtio_device.sort_by(|a, b| a.addr().cmp(&b.addr()));
// Current address allocation strategy in cloud-hypervisor is: the first created device
// will be allocated to higher address. Here we reverse the vector to make sure that
// the older created device will appear in front of the newer created device in FDT.
ordered_virtio_device.reverse();
for ordered_device_info in ordered_virtio_device.drain(..) {
create_virtio_node(fdt, ordered_device_info)?;
}
@@ -526,85 +551,45 @@ fn create_devices_node<T: DeviceInfoForFDT + Clone + Debug>(
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::aarch64::gic::create_gic;
use crate::aarch64::layout;
use kvm_ioctls::Kvm;
fn create_pci_nodes(fdt: &mut Vec<u8>, pci_device_base: u64, pci_device_size: u64) -> Result<()> {
// Add node for PCIe controller.
// See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
// and https://elinux.org/Device_Tree_Usage.
let ranges = generate_prop32(&[
// mmio addresses
0x2000000, // (ss = 10: 32-bit memory space)
(MEM_32BIT_DEVICES_START.0 >> 32) as u32, // PCI address
MEM_32BIT_DEVICES_START.0 as u32,
(MEM_32BIT_DEVICES_START.0 >> 32) as u32, // CPU address
MEM_32BIT_DEVICES_START.0 as u32,
(MEM_32BIT_DEVICES_SIZE >> 32) as u32, // size
MEM_32BIT_DEVICES_SIZE as u32,
// device addresses
0x3000000, // (ss = 11: 64-bit memory space)
(pci_device_base >> 32) as u32, // PCI address
pci_device_base as u32,
(pci_device_base >> 32) as u32, // CPU address
pci_device_base as u32,
(pci_device_size >> 32) as u32, // size
pci_device_size as u32,
]);
let bus_range = generate_prop32(&[0, 0]); // Only bus 0
let reg = generate_prop64(&[PCI_MMCONFIG_START.0, PCI_MMCONFIG_SIZE]);
const LEN: u64 = 4096;
append_begin_node(fdt, "pci")?;
append_property_string(fdt, "compatible", "pci-host-ecam-generic")?;
append_property_string(fdt, "device_type", "pci")?;
append_property(fdt, "ranges", &ranges)?;
append_property(fdt, "bus-range", &bus_range)?;
append_property_u32(fdt, "#address-cells", 3)?;
append_property_u32(fdt, "#size-cells", 2)?;
append_property(fdt, "reg", &reg)?;
append_property_u32(fdt, "#interrupt-cells", 1)?;
append_property_null(fdt, "interrupt-map")?;
append_property_null(fdt, "interrupt-map-mask")?;
append_property_null(fdt, "dma-coherent")?;
append_property_u32(fdt, "msi-parent", MSI_PHANDLE)?;
append_end_node(fdt)?;
#[derive(Clone, Debug)]
pub struct MMIODeviceInfo {
addr: u64,
irq: u32,
}
impl DeviceInfoForFDT for MMIODeviceInfo {
fn addr(&self) -> u64 {
self.addr
}
fn irq(&self) -> u32 {
self.irq
}
fn length(&self) -> u64 {
LEN
}
}
// The `load` function from the `device_tree` will mistakenly check the actual size
// of the buffer with the allocated size. This works around that.
fn set_size(buf: &mut [u8], pos: usize, val: usize) {
buf[pos] = ((val >> 24) & 0xff) as u8;
buf[pos + 1] = ((val >> 16) & 0xff) as u8;
buf[pos + 2] = ((val >> 8) & 0xff) as u8;
buf[pos + 3] = (val & 0xff) as u8;
}
#[test]
fn test_create_fdt_with_devices() {
let mut regions = Vec::new();
regions.push((
GuestAddress(layout::RAM_64BIT_START),
(layout::FDT_MAX_SIZE + 0x1000) as usize,
));
let mem = GuestMemoryMmap::from_ranges(&regions).expect("Cannot initialize memory");
let dev_info: HashMap<(DeviceType, std::string::String), MMIODeviceInfo> = [
(
(DeviceType::Serial, DeviceType::Serial.to_string()),
MMIODeviceInfo { addr: 0x00, irq: 1 },
),
(
(DeviceType::Virtio(1), "virtio".to_string()),
MMIODeviceInfo {
addr: 0x00 + LEN,
irq: 2,
},
),
(
(DeviceType::RTC, "rtc".to_string()),
MMIODeviceInfo {
addr: 0x00 + 2 * LEN,
irq: 3,
},
),
]
.iter()
.cloned()
.collect();
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let gic = create_gic(&vm, 1).unwrap();
assert!(create_fdt(
&mem,
&CString::new("console=tty0").unwrap(),
vec![0],
&dev_info,
&gic,
&None,
)
.is_ok())
}
Ok(())
}

View File

@@ -1,33 +1,22 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use std::{boxed::Box, result};
use kvm_ioctls::{DeviceFd, VmFd};
use super::gicv2::GICv2;
use super::gicv3::GICv3;
use std::result;
use std::sync::Arc;
/// Errors thrown while setting up the GIC.
#[derive(Debug)]
pub enum Error {
/// Error while calling KVM ioctl for setting up the global interrupt controller.
CreateGIC(kvm_ioctls::Error),
CreateGIC(hypervisor::HypervisorVmError),
/// Error while setting device attributes for the GIC.
SetDeviceAttribute(kvm_ioctls::Error),
SetDeviceAttribute(hypervisor::HypervisorDeviceError),
}
type Result<T> = result::Result<T, Error>;
/// Trait for GIC devices.
pub trait GICDevice: Send + Sync {
/// Returns the file descriptor of the GIC device
fn device_fd(&self) -> &DeviceFd;
/// Returns an array with GIC device properties
fn device_properties(&self) -> &[u64];
/// Returns the number of vCPUs this GIC handles
fn vcpu_count(&self) -> u64;
pub trait GICDevice {
/// Returns the hypervisor agnostic Device of the GIC device
fn device(&self) -> &Arc<dyn hypervisor::Device>;
/// Returns the fdt compatibility property of the device
fn fdt_compatibility(&self) -> &str;
@@ -35,125 +24,151 @@ pub trait GICDevice: Send + Sync {
/// Returns the maint_irq fdt property of the device
fn fdt_maint_irq(&self) -> u32;
/// Returns the GIC version of the device
fn version() -> u32
where
Self: Sized;
/// Returns an array with GIC device properties
fn device_properties(&self) -> &[u64];
/// Create the GIC device object
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice>
where
Self: Sized;
/// Returns the number of vCPUs this GIC handles
fn vcpu_count(&self) -> u64;
/// Setup the device-specific attributes
fn init_device_attributes(gic_device: &Box<dyn GICDevice>) -> Result<()>
where
Self: Sized;
/// Initialize a GIC device
fn init_device(vm: &VmFd) -> Result<DeviceFd>
where
Self: Sized,
{
let mut gic_device = kvm_bindings::kvm_create_device {
type_: Self::version(),
fd: 0,
flags: 0,
};
vm.create_device(&mut gic_device).map_err(Error::CreateGIC)
/// Returns whether the GIC device is MSI compatible or not
fn msi_compatible(&self) -> bool {
false
}
/// Set a GIC device attribute
fn set_device_attribute(
fd: &DeviceFd,
group: u32,
attr: u64,
addr: u64,
flags: u32,
) -> Result<()>
where
Self: Sized,
{
let attr = kvm_bindings::kvm_device_attr {
group: group,
attr: attr,
addr: addr,
flags: flags,
};
fd.set_device_attr(&attr)
.map_err(Error::SetDeviceAttribute)?;
Ok(())
/// Returns the MSI compatibility property of the device
fn msi_compatiblility(&self) -> &str {
""
}
/// Finalize the setup of a GIC device
fn finalize_device(gic_device: &Box<dyn GICDevice>) -> Result<()>
where
Self: Sized,
{
/* We need to tell the kernel how many irqs to support with this vgic.
* See the `layout` module for details.
*/
let nr_irqs: u32 = super::layout::IRQ_MAX - super::layout::IRQ_BASE + 1;
let nr_irqs_ptr = &nr_irqs as *const u32;
Self::set_device_attribute(
gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
0,
nr_irqs_ptr as u64,
0,
)?;
/* Finalize the GIC.
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
*/
Self::set_device_attribute(
gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0,
0,
)?;
Ok(())
}
/// Method to initialize the GIC device
fn new(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>>
where
Self: Sized,
{
let vgic_fd = Self::init_device(vm)?;
let device = Self::create_device(vgic_fd, vcpu_count);
Self::init_device_attributes(&device)?;
Self::finalize_device(&device)?;
Ok(device)
/// Returns the MSI reg property of the device
fn msi_properties(&self) -> &[u64] {
&[]
}
}
/// Create a GIC device.
///
/// It will try to create by default a GICv3 device. If that fails it will try
/// to fall-back to a GICv2 device.
pub fn create_gic(vm: &VmFd, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
GICv3::new(vm, vcpu_count).or_else(|_| GICv2::new(vm, vcpu_count))
}
pub mod kvm {
use super::GICDevice;
use super::Result;
use crate::aarch64::gicv2::kvm::KvmGICv2;
use crate::aarch64::gicv3::kvm::KvmGICv3;
use crate::aarch64::gicv3_its::kvm::KvmGICv3ITS;
use crate::layout;
use hypervisor::kvm::kvm_bindings;
use std::boxed::Box;
use std::sync::Arc;
#[cfg(test)]
mod tests {
/// Trait for GIC devices.
pub trait KvmGICDevice: Send + Sync + GICDevice {
/// Returns the GIC version of the device
fn version() -> u32;
use super::*;
use kvm_ioctls::Kvm;
/// Create the GIC device object
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GICDevice>;
#[test]
fn test_create_gic() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
assert!(create_gic(&vm, 1).is_ok());
/// Setup the device-specific attributes
fn init_device_attributes(
vm: &Arc<dyn hypervisor::Vm>,
gic_device: &Box<dyn GICDevice>,
) -> Result<()>;
/// Initialize a GIC device
fn init_device(vm: &Arc<dyn hypervisor::Vm>) -> Result<Arc<dyn hypervisor::Device>> {
let mut gic_device = kvm_bindings::kvm_create_device {
type_: Self::version(),
fd: 0,
flags: 0,
};
vm.create_device(&mut gic_device)
.map_err(super::Error::CreateGIC)
}
/// Set a GIC device attribute
fn set_device_attribute(
device: &Arc<dyn hypervisor::Device>,
group: u32,
attr: u64,
addr: u64,
flags: u32,
) -> Result<()> {
let attr = kvm_bindings::kvm_device_attr {
group: group,
attr: attr,
addr: addr,
flags: flags,
};
device
.set_device_attr(&attr)
.map_err(super::Error::SetDeviceAttribute)?;
Ok(())
}
/// Finalize the setup of a GIC device
fn finalize_device(gic_device: &Box<dyn GICDevice>) -> Result<()> {
/* We need to tell the kernel how many irqs to support with this vgic.
* See the `layout` module for details.
*/
let nr_irqs: u32 = layout::IRQ_MAX - layout::IRQ_BASE + 1;
let nr_irqs_ptr = &nr_irqs as *const u32;
Self::set_device_attribute(
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
0,
nr_irqs_ptr as u64,
0,
)?;
/* Finalize the GIC.
* See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
*/
Self::set_device_attribute(
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0,
0,
)?;
Ok(())
}
/// Method to initialize the GIC device
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);
Self::init_device_attributes(vm, &device)?;
Self::finalize_device(&device)?;
Ok(device)
}
}
/// Create a GIC device.
///
/// It will try to create by default a GICv3 device. If that fails it will try
/// to fall-back to a GICv2 device.
pub fn create_gic(
vm: &Arc<dyn hypervisor::Vm>,
vcpu_count: u64,
its_required: bool,
) -> Result<Box<dyn GICDevice>> {
if its_required {
KvmGICv3ITS::new(vm, vcpu_count)
} else {
KvmGICv3ITS::new(vm, vcpu_count).or_else(|_| {
debug!("Failed to create GICv3-ITS, will try GICv3 instead.");
KvmGICv3::new(vm, vcpu_count).or_else(|_| {
debug!("Failed to create GICv3, will try GICv2 instead.");
KvmGICv2::new(vm, vcpu_count)
})
})
}
}
}

View File

@@ -1,114 +1,124 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use std::{boxed::Box, result};
pub mod kvm {
use crate::aarch64::gic::kvm::KvmGICDevice;
use crate::aarch64::gic::{Error, GICDevice};
use std::{boxed::Box, result};
type Result<T> = result::Result<T, Error>;
use crate::layout;
use hypervisor::kvm::kvm_bindings;
use std::sync::Arc;
use kvm_ioctls::DeviceFd;
/// Represent a GIC v2 device
pub struct KvmGICv2 {
/// The hypervisor agnostic device
device: Arc<dyn hypervisor::Device>,
use super::gic::{Error, GICDevice};
/// GIC device properties, to be used for setting up the fdt entry
properties: [u64; 4],
type Result<T> = result::Result<T, Error>;
/// Represent a GIC v2 device
pub struct GICv2 {
/// The file descriptor for the KVM device
fd: DeviceFd,
/// GIC device properties, to be used for setting up the fdt entry
properties: [u64; 4],
/// Number of CPUs handled by the device
vcpu_count: u64,
}
impl GICv2 {
// Unfortunately bindgen omits defines that are based on other defines.
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000;
const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000;
// Device trees specific constants
const ARCH_GIC_V2_MAINT_IRQ: u32 = 8;
/// Get the address of the GICv2 distributor.
const fn get_dist_addr() -> u64 {
super::layout::MAPPED_IO_START - GICv2::KVM_VGIC_V2_DIST_SIZE
/// Number of CPUs handled by the device
vcpu_count: u64,
}
/// Get the size of the GIC_v2 distributor.
const fn get_dist_size() -> u64 {
GICv2::KVM_VGIC_V2_DIST_SIZE
impl KvmGICv2 {
// Unfortunately bindgen omits defines that are based on other defines.
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000;
const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000;
// Device trees specific constants
const ARCH_GIC_V2_MAINT_IRQ: u32 = 8;
/// Get the address of the GICv2 distributor.
const fn get_dist_addr() -> u64 {
layout::MAPPED_IO_START - KvmGICv2::KVM_VGIC_V2_DIST_SIZE
}
/// Get the size of the GIC_v2 distributor.
const fn get_dist_size() -> u64 {
KvmGICv2::KVM_VGIC_V2_DIST_SIZE
}
/// Get the address of the GIC_v2 CPU.
const fn get_cpu_addr() -> u64 {
KvmGICv2::get_dist_addr() - KvmGICv2::KVM_VGIC_V2_CPU_SIZE
}
/// Get the size of the GIC_v2 CPU.
const fn get_cpu_size() -> u64 {
KvmGICv2::KVM_VGIC_V2_CPU_SIZE
}
}
/// Get the address of the GIC_v2 CPU.
const fn get_cpu_addr() -> u64 {
GICv2::get_dist_addr() - GICv2::KVM_VGIC_V2_CPU_SIZE
impl GICDevice for KvmGICv2 {
fn device(&self) -> &Arc<dyn hypervisor::Device> {
&self.device
}
fn device_properties(&self) -> &[u64] {
&self.properties
}
fn fdt_compatibility(&self) -> &str {
"arm,gic-400"
}
fn fdt_maint_irq(&self) -> u32 {
KvmGICv2::ARCH_GIC_V2_MAINT_IRQ
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
}
/// Get the size of the GIC_v2 CPU.
const fn get_cpu_size() -> u64 {
GICv2::KVM_VGIC_V2_CPU_SIZE
}
}
impl GICDevice for GICv2 {
fn version() -> u32 {
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2
}
fn device_fd(&self) -> &DeviceFd {
&self.fd
}
fn device_properties(&self) -> &[u64] {
&self.properties
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
fn fdt_compatibility(&self) -> &str {
"arm,gic-400"
}
fn fdt_maint_irq(&self) -> u32 {
GICv2::ARCH_GIC_V2_MAINT_IRQ
}
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> {
Box::new(GICv2 {
fd: fd,
properties: [
GICv2::get_dist_addr(),
GICv2::get_dist_size(),
GICv2::get_cpu_addr(),
GICv2::get_cpu_size(),
],
vcpu_count: vcpu_count,
})
}
fn init_device_attributes(gic_device: &Box<dyn GICDevice>) -> Result<()> {
/* Setting up the distributor attribute.
We are placing the GIC below 1GB so we need to substract the size of the distributor. */
Self::set_device_attribute(
&gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST),
&GICv2::get_dist_addr() as *const u64 as u64,
0,
)?;
/* Setting up the CPU attribute. */
Self::set_device_attribute(
&gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU),
&GICv2::get_cpu_addr() as *const u64 as u64,
0,
)?;
Ok(())
impl KvmGICDevice for KvmGICv2 {
fn version() -> u32 {
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2
}
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GICDevice> {
Box::new(KvmGICv2 {
device: device,
properties: [
KvmGICv2::get_dist_addr(),
KvmGICv2::get_dist_size(),
KvmGICv2::get_cpu_addr(),
KvmGICv2::get_cpu_size(),
],
vcpu_count: vcpu_count,
})
}
fn init_device_attributes(
_vm: &Arc<dyn hypervisor::Vm>,
gic_device: &Box<dyn GICDevice>,
) -> Result<()> {
/* Setting up the distributor attribute.
We are placing the GIC below 1GB so we need to substract the size of the distributor. */
Self::set_device_attribute(
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST),
&KvmGICv2::get_dist_addr() as *const u64 as u64,
0,
)?;
/* Setting up the CPU attribute. */
Self::set_device_attribute(
gic_device.device(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU),
&KvmGICv2::get_cpu_addr() as *const u64 as u64,
0,
)?;
Ok(())
}
}
}

View File

@@ -1,117 +1,128 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use std::{boxed::Box, result};
pub mod kvm {
use crate::aarch64::gic::kvm::KvmGICDevice;
use crate::aarch64::gic::{Error, GICDevice};
use crate::layout;
use hypervisor::kvm::kvm_bindings;
use std::sync::Arc;
use std::{boxed::Box, result};
type Result<T> = result::Result<T, Error>;
use kvm_ioctls::DeviceFd;
pub struct KvmGICv3 {
/// The hypervisor agnostic device
device: Arc<dyn hypervisor::Device>,
use super::gic::{Error, GICDevice};
/// GIC device properties, to be used for setting up the fdt entry
properties: [u64; 4],
type Result<T> = result::Result<T, Error>;
pub struct GICv3 {
/// The file descriptor for the KVM device
fd: DeviceFd,
/// GIC device properties, to be used for setting up the fdt entry
properties: [u64; 4],
/// Number of CPUs handled by the device
vcpu_count: u64,
}
impl GICv3 {
// Unfortunately bindgen omits defines that are based on other defines.
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
const SZ_64K: u64 = 0x0001_0000;
const KVM_VGIC_V3_DIST_SIZE: u64 = GICv3::SZ_64K;
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * GICv3::SZ_64K);
// Device trees specific constants
const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
/// Get the address of the GIC distributor.
fn get_dist_addr() -> u64 {
super::layout::MAPPED_IO_START - GICv3::KVM_VGIC_V3_DIST_SIZE
/// Number of CPUs handled by the device
vcpu_count: u64,
}
/// Get the size of the GIC distributor.
fn get_dist_size() -> u64 {
GICv3::KVM_VGIC_V3_DIST_SIZE
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);
// 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
}
/// Get the size of the GIC distributor.
pub fn get_dist_size() -> u64 {
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)
}
/// Get the size of the GIC redistributors.
pub fn get_redists_size(vcpu_count: u64) -> u64 {
vcpu_count * KvmGICv3::KVM_VGIC_V3_REDIST_SIZE
}
}
/// Get the address of the GIC redistributors.
fn get_redists_addr(vcpu_count: u64) -> u64 {
GICv3::get_dist_addr() - GICv3::get_redists_size(vcpu_count)
impl GICDevice for KvmGICv3 {
fn device(&self) -> &Arc<dyn hypervisor::Device> {
&self.device
}
fn fdt_compatibility(&self) -> &str {
"arm,gic-v3"
}
fn fdt_maint_irq(&self) -> u32 {
KvmGICv3::ARCH_GIC_V3_MAINT_IRQ
}
fn device_properties(&self) -> &[u64] {
&self.properties
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
}
/// Get the size of the GIC redistributors.
fn get_redists_size(vcpu_count: u64) -> u64 {
vcpu_count * GICv3::KVM_VGIC_V3_REDIST_SIZE
}
}
impl GICDevice for GICv3 {
fn version() -> u32 {
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
}
fn device_fd(&self) -> &DeviceFd {
&self.fd
}
fn device_properties(&self) -> &[u64] {
&self.properties
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
fn fdt_compatibility(&self) -> &str {
"arm,gic-v3"
}
fn fdt_maint_irq(&self) -> u32 {
GICv3::ARCH_GIC_V3_MAINT_IRQ
}
fn create_device(fd: DeviceFd, vcpu_count: u64) -> Box<dyn GICDevice> {
Box::new(GICv3 {
fd: fd,
properties: [
GICv3::get_dist_addr(),
GICv3::get_dist_size(),
GICv3::get_redists_addr(vcpu_count),
GICv3::get_redists_size(vcpu_count),
],
vcpu_count: vcpu_count,
})
}
fn init_device_attributes(gic_device: &Box<dyn GICDevice>) -> Result<()> {
/* Setting up the distributor attribute.
We are placing the GIC below 1GB so we need to substract the size of the distributor.
*/
Self::set_device_attribute(
&gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
&GICv3::get_dist_addr() as *const u64 as u64,
0,
)?;
/* Setting up the redistributors' attribute.
We are calculating here the start of the redistributors address. We have one per CPU.
*/
Self::set_device_attribute(
&gic_device.device_fd(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
&GICv3::get_redists_addr(u64::from(gic_device.vcpu_count())) as *const u64 as u64,
0,
)?;
Ok(())
impl KvmGICDevice for KvmGICv3 {
fn version() -> u32 {
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
}
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GICDevice> {
Box::new(KvmGICv3 {
device: device,
properties: [
KvmGICv3::get_dist_addr(),
KvmGICv3::get_dist_size(),
KvmGICv3::get_redists_addr(vcpu_count),
KvmGICv3::get_redists_size(vcpu_count),
],
vcpu_count: vcpu_count,
})
}
fn init_device_attributes(
_vm: &Arc<dyn hypervisor::Vm>,
gic_device: &Box<dyn GICDevice>,
) -> Result<()> {
/* Setting up the distributor attribute.
We are placing the GIC below 1GB so we need to substract the size of the distributor.
*/
Self::set_device_attribute(
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,
0,
)?;
/* Setting up the redistributors' attribute.
We are calculating here the start of the redistributors address. We have one per CPU.
*/
Self::set_device_attribute(
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(u64::from(gic_device.vcpu_count())) as *const u64
as u64,
0,
)?;
Ok(())
}
}
}

View File

@@ -0,0 +1,133 @@
// Copyright 2020 ARM Limited
// SPDX-License-Identifier: Apache-2.0
pub mod kvm {
use std::sync::Arc;
use std::{boxed::Box, result};
type Result<T> = result::Result<T, Error>;
use crate::aarch64::gic::kvm::KvmGICDevice;
use crate::aarch64::gic::{Error, GICDevice};
use crate::aarch64::gicv3::kvm::KvmGICv3;
use hypervisor::kvm::kvm_bindings;
pub struct KvmGICv3ITS {
/// The hypervisor agnostic device
device: Arc<dyn hypervisor::Device>,
/// GIC device properties, to be used for setting up the fdt entry
gic_properties: [u64; 4],
/// MSI device properties, to be used for setting up the fdt entry
msi_properties: [u64; 2],
/// Number of CPUs handled by the device
vcpu_count: u64,
}
impl KvmGICv3ITS {
const KVM_VGIC_V3_ITS_SIZE: u64 = (2 * KvmGICv3::SZ_64K);
fn get_msi_size() -> u64 {
KvmGICv3ITS::KVM_VGIC_V3_ITS_SIZE
}
fn get_msi_addr(vcpu_count: u64) -> u64 {
KvmGICv3::get_redists_addr(vcpu_count) - KvmGICv3ITS::get_msi_size()
}
}
impl GICDevice for KvmGICv3ITS {
fn device(&self) -> &Arc<dyn hypervisor::Device> {
&self.device
}
fn fdt_compatibility(&self) -> &str {
"arm,gic-v3"
}
fn msi_compatible(&self) -> bool {
true
}
fn msi_compatiblility(&self) -> &str {
"arm,gic-v3-its"
}
fn fdt_maint_irq(&self) -> u32 {
KvmGICv3::ARCH_GIC_V3_MAINT_IRQ
}
fn msi_properties(&self) -> &[u64] {
&self.msi_properties
}
fn device_properties(&self) -> &[u64] {
&self.gic_properties
}
fn vcpu_count(&self) -> u64 {
self.vcpu_count
}
}
impl KvmGICDevice for KvmGICv3ITS {
fn version() -> u32 {
KvmGICv3::version()
}
fn create_device(
device: Arc<dyn hypervisor::Device>,
vcpu_count: u64,
) -> Box<dyn GICDevice> {
Box::new(KvmGICv3ITS {
device: device,
gic_properties: [
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(),
],
vcpu_count: vcpu_count,
})
}
fn init_device_attributes(
vm: &Arc<dyn hypervisor::Vm>,
gic_device: &Box<dyn GICDevice>,
) -> Result<()> {
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,
fd: 0,
flags: 0,
};
let its_fd = vm
.create_device(&mut its_device)
.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(u64::from(gic_device.vcpu_count())) as *const u64 as u64,
0,
)?;
Self::set_device_attribute(
&its_fd,
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT),
0,
0,
)?;
Ok(())
}
}
}

View File

@@ -8,6 +8,7 @@ pub mod fdt;
pub mod gic;
mod gicv2;
mod gicv3;
mod gicv3_its;
/// Layout for this aarch64 system.
pub mod layout;
/// Logic for configuring aarch64 registers.
@@ -16,10 +17,11 @@ pub mod regs;
pub use self::fdt::DeviceInfoForFDT;
use crate::DeviceType;
use crate::RegionType;
use kvm_ioctls::*;
use hypervisor::kvm::kvm_bindings;
use std::collections::HashMap;
use std::ffi::CStr;
use std::fmt::Debug;
use std::sync::Arc;
use vm_memory::{
Address, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, GuestMemoryMmap,
GuestUsize,
@@ -34,17 +36,17 @@ pub enum Error {
/// Failed to create a GIC.
SetupGIC(gic::Error),
/// Failed to compute the initrd address.
InitrdAddress,
/// Failed to compute the initramfs address.
InitramfsAddress,
/// Error configuring the general purpose registers
REGSConfiguration(regs::Error),
/// Error fetching prefered target
VcpuArmPreferredTarget(kvm_ioctls::Error),
VcpuArmPreferredTarget(hypervisor::HypervisorVmError),
/// Error doing Vcpu Init on Arm.
VcpuArmInit(kvm_ioctls::Error),
VcpuArmInit(hypervisor::HypervisorCpuError),
}
impl From<Error> for super::Error {
@@ -63,17 +65,16 @@ pub struct EntryPoint {
/// Configure the specified VCPU, and return its MPIDR.
pub fn configure_vcpu(
fd: &VcpuFd,
fd: &Arc<dyn hypervisor::Vcpu>,
id: u8,
vm_fd: &VmFd,
vm: &Arc<dyn hypervisor::Vm>,
kernel_entry_point: Option<EntryPoint>,
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
) -> super::Result<u64> {
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
// This reads back the kernel's preferred target type.
vm_fd
.get_preferred_target(&mut kvi)
vm.get_preferred_target(&mut kvi)
.map_err(Error::VcpuArmPreferredTarget)?;
// We already checked that the capability is supported.
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2;
@@ -135,32 +136,56 @@ 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)]
#[allow(unused_variables)]
pub fn configure_system<T: DeviceInfoForFDT + Clone + Debug>(
vm_fd: &VmFd,
vm: &Arc<dyn hypervisor::Vm>,
guest_mem: &GuestMemoryMmap,
cmdline_cstring: &CStr,
vcpu_count: u64,
vcpu_mpidr: Vec<u64>,
device_info: &HashMap<(DeviceType, String), T>,
initrd: &Option<super::InitramfsConfig>,
pci_space_address: &Option<(u64, u64)>,
) -> super::Result<()> {
let gic_device = gic::create_gic(vm_fd, vcpu_count).map_err(Error::SetupGIC)?;
// If pci_space_address is present, it means PCI devices are used ("pci" feature enabled).
// Then GITv3-ITS is required for MSI messaging.
// Otherwise ("mmio" feature enabled), any version of GIC is OK.
let gic_device = gic::kvm::create_gic(vm, vcpu_count, pci_space_address.is_some())
.map_err(Error::SetupGIC)?;
let dtb = fdt::create_fdt(
fdt::create_fdt(
guest_mem,
cmdline_cstring,
vcpu_mpidr,
device_info,
&gic_device,
initrd,
pci_space_address,
)
.map_err(Error::SetupFDT)?;
Ok(())
}
/// Returns the memory address where the initramfs could be loaded.
pub fn initramfs_load_addr(
guest_mem: &GuestMemoryMmap,
initramfs_size: usize,
) -> super::Result<u64> {
let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1);
match GuestAddress(get_fdt_addr(&guest_mem))
.checked_sub(round_to_pagesize(initramfs_size) as u64)
{
Some(offset) => {
if guest_mem.address_in_range(offset) {
Ok(offset.raw_value())
} else {
Err(super::Error::AArch64Setup(Error::InitramfsAddress))
}
}
None => Err(super::Error::AArch64Setup(Error::InitramfsAddress)),
}
}
/// Returns the memory address where the kernel could be loaded.
pub fn get_kernel_start() -> u64 {
layout::RAM_64BIT_START
@@ -200,13 +225,6 @@ pub fn get_host_cpu_phys_bits() -> u8 {
40
}
pub fn check_required_kvm_extensions(kvm: &Kvm) -> super::Result<()> {
if !kvm.check_extension(Cap::SignalMsi) {
return Err(super::Error::CapabilityMissing(Cap::SignalMsi));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -5,27 +5,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.
use std::{mem, result};
use super::get_fdt_addr;
use kvm_bindings::{
use hypervisor::kvm::kvm_bindings::{
user_pt_regs, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_CRM_MASK,
KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT,
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK,
KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT,
KVM_REG_ARM_CORE, KVM_REG_SIZE_U64,
};
use kvm_ioctls::VcpuFd;
use std::sync::Arc;
use std::{mem, result};
use vm_memory::GuestMemoryMmap;
/// Errors thrown while setting aarch64 registers.
#[derive(Debug)]
pub enum Error {
/// Failed to set core register (PC, PSTATE or general purpose ones).
SetCoreRegister(kvm_ioctls::Error),
SetCoreRegister(hypervisor::HypervisorCpuError),
/// Failed to get a system register.
GetSysRegister(kvm_ioctls::Error),
GetSysRegister(hypervisor::HypervisorCpuError),
}
type Result<T> = result::Result<T, Error>;
@@ -122,7 +120,12 @@ arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
/// * `cpu_id` - Index of current vcpu.
/// * `boot_ip` - Starting instruction pointer.
/// * `mem` - Reserved DRAM for current VM.
pub fn setup_regs(vcpu: &VcpuFd, cpu_id: u8, boot_ip: u64, mem: &GuestMemoryMmap) -> Result<()> {
pub fn setup_regs(
vcpu: &Arc<dyn hypervisor::Vcpu>,
cpu_id: u8,
boot_ip: u64,
mem: &GuestMemoryMmap,
) -> Result<()> {
// Get the register index of the PSTATE (Processor State) register.
vcpu.set_one_reg(arm64_core_reg!(pstate), PSTATE_FAULT_BITS_64)
.map_err(Error::SetCoreRegister)?;
@@ -148,51 +151,6 @@ pub fn setup_regs(vcpu: &VcpuFd, cpu_id: u8, boot_ip: u64, mem: &GuestMemoryMmap
/// # Arguments
///
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
pub fn read_mpidr(vcpu: &VcpuFd) -> Result<u64> {
pub fn read_mpidr(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<u64> {
vcpu.get_one_reg(MPIDR_EL1).map_err(Error::GetSysRegister)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::aarch64::layout;
use kvm_ioctls::Kvm;
use vm_memory::{GuestAddress, GuestMemoryMmap};
#[test]
fn test_setup_regs() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
let mut regions = Vec::new();
regions.push((
GuestAddress(layout::RAM_64BIT_START),
(layout::FDT_MAX_SIZE + 0x1000) as usize,
));
let mem = GuestMemoryMmap::from_ranges(&regions).expect("Cannot initialize memory");
match setup_regs(&vcpu, 0, 0x0, &mem).unwrap_err() {
Error::SetCoreRegister(ref e) => assert_eq!(e.errno(), libc::ENOEXEC),
_ => panic!("Expected to receive Error::SetCoreRegister"),
}
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
vm.get_preferred_target(&mut kvi).unwrap();
vcpu.vcpu_init(&kvi).unwrap();
assert!(setup_regs(&vcpu, 0, 0x0, &mem).is_ok());
}
#[test]
fn test_read_mpidr() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
vm.get_preferred_target(&mut kvi).unwrap();
// Must fail when vcpu is not initialized yet.
assert!(read_mpidr(&vcpu).is_err());
vcpu.vcpu_init(&kvi).unwrap();
assert_eq!(read_mpidr(&vcpu).unwrap(), 0x80000000);
}
}

View File

@@ -15,18 +15,16 @@
)]
extern crate byteorder;
extern crate kvm_bindings;
extern crate kvm_ioctls;
extern crate hypervisor;
extern crate libc;
extern crate vm_memory;
#[macro_use]
extern crate log;
#[cfg(feature = "acpi")]
extern crate acpi_tables;
extern crate arch_gen;
extern crate linux_loader;
extern crate vm_memory;
use kvm_ioctls::*;
use std::fmt;
use std::result;
@@ -57,8 +55,6 @@ pub enum Error {
ModlistSetup(vm_memory::GuestMemoryError),
/// RSDP Beyond Guest Memory
RSDPPastRamEnd,
/// Capability missing
CapabilityMissing(Cap),
}
/// Type for returning public functions outcome.
@@ -89,8 +85,8 @@ pub mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use aarch64::{
arch_memory_regions, check_required_kvm_extensions, configure_system, configure_vcpu,
fdt::DeviceInfoForFDT, get_host_cpu_phys_bits, get_kernel_start, layout,
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,
};
@@ -99,9 +95,9 @@ pub mod x86_64;
#[cfg(target_arch = "x86_64")]
pub use x86_64::{
arch_memory_regions, check_required_kvm_extensions, configure_system, configure_vcpu,
get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
layout::CMDLINE_START, regs, BootProtocol, CpuidPatch, CpuidReg, EntryPoint,
arch_memory_regions, configure_system, configure_vcpu, get_host_cpu_phys_bits,
initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, regs,
BootProtocol, CpuidPatch, CpuidReg, EntryPoint,
};
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.

View File

@@ -8,8 +8,7 @@
// found in the LICENSE-BSD-3-Clause file.
// For GDT details see arch/x86/include/asm/segment.h
use kvm_bindings::kvm_segment;
use hypervisor::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 {
@@ -88,14 +87,14 @@ fn get_type(entry: u64) -> u8 {
((entry & 0x00000F0000000000) >> 40) as u8
}
/// Automatically build the kvm struct for SET_SREGS from the kernel bit fields.
/// Automatically build the struct for SET_SREGS from the kernel bit fields.
///
/// # Arguments
///
/// * `entry` - The gdt entry.
/// * `table_index` - Index of the entry in the gdt table.
pub fn kvm_segment_from_gdt(entry: u64, table_index: u8) -> kvm_segment {
kvm_segment {
pub fn segment_from_gdt(entry: u64, table_index: u8) -> SegmentRegister {
SegmentRegister {
base: get_base(entry),
limit: get_limit(entry),
selector: (table_index * 8) as u16,
@@ -122,7 +121,7 @@ mod tests {
#[test]
fn field_parse() {
let gdt = gdt_entry(0xA09B, 0x100000, 0xfffff);
let seg = kvm_segment_from_gdt(gdt, 0);
let seg = segment_from_gdt(gdt, 0);
// 0xA09B
// 'A'
assert_eq!(0x1, seg.g);

View File

@@ -8,27 +8,27 @@
use std::io::Cursor;
use std::mem;
use std::result;
use std::sync::Arc;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use kvm_bindings::kvm_lapic_state;
use kvm_ioctls;
use hypervisor::x86_64::LapicState;
#[derive(Debug)]
pub enum Error {
GetLapic(kvm_ioctls::Error),
SetLapic(kvm_ioctls::Error),
GetLapic(anyhow::Error),
SetLapic(anyhow::Error),
}
pub type Result<T> = result::Result<T, Error>;
pub type Result<T> = result::Result<T, hypervisor::HypervisorCpuError>;
// Defines poached from apicdef.h kernel header.
const APIC_LVT0: usize = 0x350;
const APIC_LVT1: usize = 0x360;
const APIC_MODE_NMI: u32 = 0x4;
const APIC_MODE_EXTINT: u32 = 0x7;
pub const APIC_LVT0: usize = 0x350;
pub const APIC_LVT1: usize = 0x360;
pub const APIC_MODE_NMI: u32 = 0x4;
pub const APIC_MODE_EXTINT: u32 = 0x7;
fn get_klapic_reg(klapic: &kvm_lapic_state, reg_offset: usize) -> u32 {
pub fn get_klapic_reg(klapic: &LapicState, reg_offset: usize) -> u32 {
let sliceu8 = unsafe {
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
// Cursors are only readable on arrays of u8, not i8(c_char).
@@ -41,7 +41,7 @@ fn get_klapic_reg(klapic: &kvm_lapic_state, reg_offset: usize) -> u32 {
.expect("Failed to read klapic register")
}
fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) {
pub fn set_klapic_reg(klapic: &mut LapicState, reg_offset: usize, value: u32) {
let sliceu8 = unsafe {
// This array is only accessed as parts of a u32 word, so interpret it as a u8 array.
// Cursors are only readable on arrays of u8, not i8(c_char).
@@ -54,7 +54,7 @@ fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) {
.expect("Failed to write klapic register")
}
fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
pub fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
((reg) & !0x700) | ((mode) << 8)
}
@@ -62,8 +62,8 @@ fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
///
/// # Arguments
/// * `vcpu` - The VCPU object to configure.
pub fn set_lint(vcpu: &kvm_ioctls::VcpuFd) -> Result<()> {
let mut klapic = vcpu.get_lapic().map_err(Error::GetLapic)?;
pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
let mut klapic = vcpu.get_lapic()?;
let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0);
set_klapic_reg(
@@ -78,24 +78,23 @@ pub fn set_lint(vcpu: &kvm_ioctls::VcpuFd) -> Result<()> {
set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI),
);
vcpu.set_lapic(&klapic).map_err(Error::SetLapic)
vcpu.set_lapic(&klapic)
}
#[cfg(test)]
mod tests {
extern crate kvm_ioctls;
extern crate rand;
use self::rand::Rng;
use super::*;
use kvm_ioctls::Kvm;
const KVM_APIC_REG_SIZE: usize = 0x400;
#[test]
fn test_set_and_get_klapic_reg() {
let reg_offset = 0x340;
let mut klapic = kvm_lapic_state::default();
let mut klapic = LapicState::default();
set_klapic_reg(&mut klapic, reg_offset, 3);
let value = get_klapic_reg(&klapic, reg_offset);
assert_eq!(value, 3);
@@ -105,7 +104,7 @@ mod tests {
#[should_panic]
fn test_set_and_get_klapic_out_of_bounds() {
let reg_offset = KVM_APIC_REG_SIZE + 10;
let mut klapic = kvm_lapic_state::default();
let mut klapic = LapicState::default();
set_klapic_reg(&mut klapic, reg_offset, 3);
}
@@ -119,40 +118,4 @@ mod tests {
let after: Vec<u32> = v.iter().map(|x| ((*x & !0x700) | ((2) << 8))).collect();
assert_eq!(v, after);
}
#[test]
fn test_setlint() {
let kvm = kvm_ioctls::Kvm::new().unwrap();
assert!(kvm.check_extension(kvm_ioctls::Cap::Irqchip));
let vm = kvm.create_vm().unwrap();
//the get_lapic ioctl will fail if there is no irqchip created beforehand.
assert!(vm.create_irq_chip().is_ok());
let vcpu = vm.create_vcpu(0).unwrap();
let klapic_before: kvm_lapic_state = vcpu.get_lapic().unwrap();
// Compute the value that is expected to represent LVT0 and LVT1.
let lint0 = get_klapic_reg(&klapic_before, APIC_LVT0);
let lint1 = get_klapic_reg(&klapic_before, APIC_LVT1);
let lint0_mode_expected = set_apic_delivery_mode(lint0, APIC_MODE_EXTINT);
let lint1_mode_expected = set_apic_delivery_mode(lint1, APIC_MODE_NMI);
set_lint(&vcpu).unwrap();
// Compute the value that represents LVT0 and LVT1 after set_lint.
let klapic_actual: kvm_lapic_state = vcpu.get_lapic().unwrap();
let lint0_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT0);
let lint1_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT1);
assert_eq!(lint0_mode_expected, lint0_mode_actual);
assert_eq!(lint1_mode_expected, lint1_mode_actual);
}
#[test]
fn test_setlint_fails() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
// 'get_lapic' ioctl triggered by the 'set_lint' function will fail if there is no
// irqchip created beforehand.
assert!(set_lint(&vcpu).is_err());
}
}

View File

@@ -70,6 +70,8 @@ pub const EBDA_START: GuestAddress = GuestAddress(0xa0000);
// ACPI RSDP table
pub const RSDP_POINTER: GuestAddress = EBDA_START;
pub const SMBIOS_START: u64 = 0xf0000; // First possible location per the spec.
// == End of "EBDA" range ==
// ** High RAM (start: 1MiB, length: 3071MiB) **

View File

@@ -6,7 +6,7 @@
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use std::sync::Arc;
mod gdt;
pub mod interrupts;
pub mod layout;
@@ -15,8 +15,7 @@ mod mptable;
pub mod regs;
use crate::InitramfsConfig;
use crate::RegionType;
use kvm_bindings::CpuId;
use kvm_ioctls::*;
use hypervisor::{CpuId, CpuIdEntry, CPUID_FLAG_VALID_INDEX};
use linux_loader::loader::bootparam::{boot_params, setup_header};
use linux_loader::loader::elf::start_info::{
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
@@ -26,6 +25,7 @@ use vm_memory::{
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
GuestMemoryMmap, GuestMemoryRegion, GuestUsize,
};
mod smbios;
#[derive(Debug, Copy, Clone)]
pub enum BootProtocol {
@@ -58,6 +58,53 @@ pub struct EntryPoint {
const E820_RAM: u32 = 1;
const E820_RESERVED: u32 = 2;
#[derive(Clone)]
pub struct SgxEpcSection {
start: GuestAddress,
size: GuestUsize,
}
impl SgxEpcSection {
pub fn new(start: GuestAddress, size: GuestUsize) -> Self {
SgxEpcSection { start, size }
}
pub fn start(&self) -> GuestAddress {
self.start
}
pub fn size(&self) -> GuestUsize {
self.size
}
}
#[derive(Clone)]
pub struct SgxEpcRegion {
start: GuestAddress,
size: GuestUsize,
epc_sections: Vec<SgxEpcSection>,
}
impl SgxEpcRegion {
pub fn new(start: GuestAddress, size: GuestUsize) -> Self {
SgxEpcRegion {
start,
size,
epc_sections: Vec::new(),
}
}
pub fn start(&self) -> GuestAddress {
self.start
}
pub fn size(&self) -> GuestUsize {
self.size
}
pub fn epc_sections(&self) -> &Vec<SgxEpcSection> {
&self.epc_sections
}
pub fn push(&mut self, epc_section: SgxEpcSection) {
self.epc_sections.push(epc_section);
}
}
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
// trait (in this case `DataInit`) where:
// * the type that is implementing the trait is foreign or
@@ -110,11 +157,23 @@ pub enum Error {
/// Error configuring the MSR registers
MSRSConfiguration(regs::Error),
/// The call to KVM_SET_CPUID2 failed.
SetSupportedCpusFailed(kvm_ioctls::Error),
/// Failed to set supported CPUs.
SetSupportedCpusFailed(anyhow::Error),
/// Cannot set the local interruption due to bad configuration.
LocalIntConfiguration(interrupts::Error),
LocalIntConfiguration(anyhow::Error),
/// Error setting up SMBIOS table
SmbiosSetup(smbios::Error),
/// Could not find any SGX EPC section
NoSgxEpcSection,
/// Missing SGX CPU feature
MissingSgxFeature,
/// Missing SGX_LC CPU feature
MissingSgxLaunchControlFeature,
}
impl From<Error> for super::Error {
@@ -152,8 +211,10 @@ impl CpuidPatch {
) {
let entries = cpuid.as_mut_slice();
let mut entry_found = false;
for entry in entries.iter_mut() {
if entry.function == function && (index == None || index.unwrap() == entry.index) {
entry_found = true;
match reg {
CpuidReg::EAX => {
entry.eax = value;
@@ -170,6 +231,38 @@ impl CpuidPatch {
}
}
}
if entry_found {
return;
}
// Entry not found, so let's add it.
if let Some(index) = index {
let mut entry = CpuIdEntry {
function,
index,
flags: CPUID_FLAG_VALID_INDEX,
..Default::default()
};
match reg {
CpuidReg::EAX => {
entry.eax = value;
}
CpuidReg::EBX => {
entry.ebx = value;
}
CpuidReg::ECX => {
entry.ecx = value;
}
CpuidReg::EDX => {
entry.edx = value;
}
}
if let Err(e) = cpuid.push(entry) {
error!("Failed adding new CPUID entry: {:?}", e);
}
}
}
pub fn patch_cpuid(cpuid: &mut CpuId, patches: Vec<CpuidPatch>) {
@@ -197,10 +290,45 @@ impl CpuidPatch {
}
}
}
pub fn is_feature_enabled(
cpuid: &CpuId,
function: u32,
index: u32,
reg: CpuidReg,
feature_bit: usize,
) -> bool {
let entries = cpuid.as_slice();
let mask = 1 << feature_bit;
for entry in entries.iter() {
if entry.function == function && entry.index == index {
let reg_val: u32;
match reg {
CpuidReg::EAX => {
reg_val = entry.eax;
}
CpuidReg::EBX => {
reg_val = entry.ebx;
}
CpuidReg::ECX => {
reg_val = entry.ecx;
}
CpuidReg::EDX => {
reg_val = entry.edx;
}
}
return (reg_val & mask) == mask;
}
}
false
}
}
pub fn configure_vcpu(
fd: &VcpuFd,
fd: &Arc<dyn hypervisor::Vcpu>,
id: u8,
kernel_entry_point: Option<EntryPoint>,
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
@@ -208,8 +336,9 @@ pub fn configure_vcpu(
) -> super::Result<()> {
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));
fd.set_cpuid2(&cpuid)
.map_err(Error::SetSupportedCpusFailed)?;
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
regs::setup_msrs(fd).map_err(Error::MSRSConfiguration)?;
if let Some(kernel_entry_point) = kernel_entry_point {
@@ -226,7 +355,7 @@ pub fn configure_vcpu(
regs::setup_sregs(&vm_memory.memory(), fd, kernel_entry_point.protocol)
.map_err(Error::SREGSConfiguration)?;
}
interrupts::set_lint(fd).map_err(Error::LocalIntConfiguration)?;
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
Ok(())
}
@@ -295,7 +424,10 @@ pub fn configure_system(
setup_hdr: Option<setup_header>,
rsdp_addr: Option<GuestAddress>,
boot_prot: BootProtocol,
sgx_epc_region: Option<SgxEpcRegion>,
) -> super::Result<()> {
smbios::setup_smbios(guest_mem).map_err(Error::SmbiosSetup)?;
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
#[cfg(not(feature = "acpi"))]
mptable::setup_mptable(guest_mem, _num_cpus).map_err(Error::MpTableSetup)?;
@@ -309,7 +441,13 @@ pub fn configure_system(
match boot_prot {
BootProtocol::PvhBoot => {
configure_pvh(guest_mem, cmdline_addr, initramfs, rsdp_addr)?;
configure_pvh(
guest_mem,
cmdline_addr,
initramfs,
rsdp_addr,
sgx_epc_region,
)?;
}
BootProtocol::LinuxBoot => {
configure_64bit_boot(
@@ -319,6 +457,7 @@ pub fn configure_system(
initramfs,
setup_hdr,
rsdp_addr,
sgx_epc_region,
)?;
}
}
@@ -331,6 +470,7 @@ fn configure_pvh(
cmdline_addr: GuestAddress,
initramfs: &Option<InitramfsConfig>,
rsdp_addr: Option<GuestAddress>,
sgx_epc_region: Option<SgxEpcRegion>,
) -> super::Result<()> {
const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336ec578;
@@ -404,6 +544,15 @@ fn configure_pvh(
E820_RESERVED,
)?;
if let Some(sgx_epc_region) = sgx_epc_region {
add_memmap_entry(
&mut memmap,
sgx_epc_region.start().raw_value(),
sgx_epc_region.size() as u64,
E820_RESERVED,
)?;
}
start_info.0.memmap_entries = memmap.len() as u32;
// Copy the vector with the memmap table to the MEMMAP_START address
@@ -470,6 +619,7 @@ fn configure_64bit_boot(
initramfs: &Option<InitramfsConfig>,
setup_hdr: Option<setup_header>,
rsdp_addr: Option<GuestAddress>,
sgx_epc_region: Option<SgxEpcRegion>,
) -> super::Result<()> {
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
const KERNEL_HDR_MAGIC: u32 = 0x53726448;
@@ -533,6 +683,15 @@ fn configure_64bit_boot(
E820_RESERVED,
)?;
if let Some(sgx_epc_region) = sgx_epc_region {
add_e820_entry(
&mut params.0,
sgx_epc_region.start().raw_value(),
sgx_epc_region.size() as u64,
E820_RESERVED,
)?;
}
if let Some(rsdp_addr) = rsdp_addr {
params.0.acpi_rsdp_addr = rsdp_addr.0;
}
@@ -615,16 +774,112 @@ pub fn get_host_cpu_phys_bits() -> u8 {
}
}
pub fn check_required_kvm_extensions(kvm: &Kvm) -> super::Result<()> {
if !kvm.check_extension(Cap::SignalMsi) {
return Err(super::Error::CapabilityMissing(Cap::SignalMsi));
pub fn update_cpuid_topology(
cpuid: &mut CpuId,
threads_per_core: u8,
cores_per_die: u8,
dies_per_package: u8,
) {
let thread_width = 8 - (threads_per_core - 1).leading_zeros();
let core_width = (8 - (cores_per_die - 1).leading_zeros()) + thread_width;
let die_width = (8 - (dies_per_package - 1).leading_zeros()) + core_width;
// CPU Topology leaf 0xb
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(0), CpuidReg::EAX, thread_width);
CpuidPatch::set_cpuid_reg(
cpuid,
0xb,
Some(0),
CpuidReg::EBX,
u32::from(threads_per_core),
);
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(0), CpuidReg::ECX, 1 << 8);
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(1), CpuidReg::EAX, die_width);
CpuidPatch::set_cpuid_reg(
cpuid,
0xb,
Some(1),
CpuidReg::EBX,
u32::from(dies_per_package * cores_per_die * threads_per_core),
);
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(1), CpuidReg::ECX, 2 << 8);
// CPU Topology leaf 0x1f
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(0), CpuidReg::EAX, thread_width);
CpuidPatch::set_cpuid_reg(
cpuid,
0x1f,
Some(0),
CpuidReg::EBX,
u32::from(threads_per_core),
);
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(0), CpuidReg::ECX, 1 << 8);
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(1), CpuidReg::EAX, core_width);
CpuidPatch::set_cpuid_reg(
cpuid,
0x1f,
Some(1),
CpuidReg::EBX,
u32::from(cores_per_die * threads_per_core),
);
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(1), CpuidReg::ECX, 2 << 8);
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(2), CpuidReg::EAX, die_width);
CpuidPatch::set_cpuid_reg(
cpuid,
0x1f,
Some(2),
CpuidReg::EBX,
u32::from(dies_per_package * cores_per_die * threads_per_core),
);
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(2), CpuidReg::ECX, 5 << 8);
}
// The goal is to update the CPUID sub-leaves to reflect the number of EPC
// sections exposed to the guest.
pub fn update_cpuid_sgx(cpuid: &mut CpuId, epc_sections: Vec<SgxEpcSection>) -> Result<(), Error> {
// Something's wrong if there's no EPC section.
if epc_sections.is_empty() {
return Err(Error::NoSgxEpcSection);
}
if !kvm.check_extension(Cap::TscDeadlineTimer) {
return Err(super::Error::CapabilityMissing(Cap::TscDeadlineTimer));
// We can't go further if the hypervisor does not support SGX feature.
if !CpuidPatch::is_feature_enabled(cpuid, 0x7, 0, CpuidReg::EBX, 2) {
return Err(Error::MissingSgxFeature);
}
if !kvm.check_extension(Cap::SplitIrqchip) {
return Err(super::Error::CapabilityMissing(Cap::SplitIrqchip));
// We can't go further if the hypervisor does not support SGX_LC feature.
if !CpuidPatch::is_feature_enabled(cpuid, 0x7, 0, CpuidReg::ECX, 30) {
return Err(Error::MissingSgxLaunchControlFeature);
}
// Get host CPUID for leaf 0x12, subleaf 0x2. This is to retrieve EPC
// properties such as confidentiality and integrity.
let leaf = unsafe { std::arch::x86_64::__cpuid_count(0x12, 0x2) };
for (i, epc_section) in epc_sections.iter().enumerate() {
let subleaf_idx = i + 2;
let start = epc_section.start().raw_value();
let size = epc_section.size() as u64;
let eax = (start & 0xffff_f000) as u32 | 0x1;
let ebx = (start >> 32) as u32;
let ecx = (size & 0xffff_f000) as u32 | (leaf.ecx & 0xf);
let edx = (size >> 32) as u32;
// CPU Topology leaf 0x12
CpuidPatch::set_cpuid_reg(cpuid, 0x12, Some(subleaf_idx as u32), CpuidReg::EAX, eax);
CpuidPatch::set_cpuid_reg(cpuid, 0x12, Some(subleaf_idx as u32), CpuidReg::EBX, ebx);
CpuidPatch::set_cpuid_reg(cpuid, 0x12, Some(subleaf_idx as u32), CpuidReg::ECX, ecx);
CpuidPatch::set_cpuid_reg(cpuid, 0x12, Some(subleaf_idx as u32), CpuidReg::EDX, edx);
}
// Add one NULL entry to terminate the dynamic list
let subleaf_idx = epc_sections.len() + 2;
// CPU Topology leaf 0x12
CpuidPatch::set_cpuid_reg(cpuid, 0x12, Some(subleaf_idx as u32), CpuidReg::EAX, 0);
CpuidPatch::set_cpuid_reg(cpuid, 0x12, Some(subleaf_idx as u32), CpuidReg::EBX, 0);
CpuidPatch::set_cpuid_reg(cpuid, 0x12, Some(subleaf_idx as u32), CpuidReg::ECX, 0);
CpuidPatch::set_cpuid_reg(cpuid, 0x12, Some(subleaf_idx as u32), CpuidReg::EDX, 0);
Ok(())
}
@@ -662,6 +917,7 @@ mod tests {
None,
Some(layout::RSDP_POINTER),
BootProtocol::LinuxBoot,
None,
);
assert!(config_err.is_err());
@@ -683,6 +939,7 @@ mod tests {
None,
None,
BootProtocol::LinuxBoot,
None,
)
.unwrap();
@@ -695,6 +952,7 @@ mod tests {
None,
None,
BootProtocol::PvhBoot,
None,
)
.unwrap();
@@ -716,6 +974,7 @@ mod tests {
None,
None,
BootProtocol::LinuxBoot,
None,
)
.unwrap();
@@ -728,6 +987,7 @@ mod tests {
None,
None,
BootProtocol::PvhBoot,
None,
)
.unwrap();
@@ -749,6 +1009,7 @@ mod tests {
None,
None,
BootProtocol::LinuxBoot,
None,
)
.unwrap();
@@ -761,6 +1022,7 @@ mod tests {
None,
None,
BootProtocol::PvhBoot,
None,
)
.unwrap();
}

View File

@@ -6,35 +6,29 @@
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use std::sync::Arc;
use std::{mem, result};
use super::gdt::{gdt_entry, kvm_segment_from_gdt};
use super::gdt::{gdt_entry, segment_from_gdt};
use super::BootProtocol;
use arch_gen::x86::msr_index;
use kvm_bindings::{kvm_fpu, kvm_msr_entry, kvm_regs, kvm_sregs, Msrs};
use kvm_ioctls::VcpuFd;
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,
};
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryMmap};
// MTRR constants
const MTRR_ENABLE: u64 = 0x800; // IA32_MTRR_DEF_TYPE MSR: E (MTRRs enabled) flag, bit 11
const MTRR_MEM_TYPE_WB: u64 = 0x6;
#[derive(Debug)]
pub enum Error {
/// Failed to get SREGs for this CPU.
GetStatusRegisters(kvm_ioctls::Error),
GetStatusRegisters(hypervisor::HypervisorCpuError),
/// Failed to set base registers for this CPU.
SetBaseRegisters(kvm_ioctls::Error),
SetBaseRegisters(hypervisor::HypervisorCpuError),
/// Failed to configure the FPU.
SetFPURegisters(kvm_ioctls::Error),
SetFPURegisters(hypervisor::HypervisorCpuError),
/// Setting up MSRs failed.
SetModelSpecificRegisters(kvm_ioctls::Error),
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
/// Failed to set SREGs for this CPU.
SetStatusRegisters(kvm_ioctls::Error),
SetStatusRegisters(hypervisor::HypervisorCpuError),
/// Checking the GDT address failed.
CheckGDTAddr,
/// Writing the GDT to RAM failed.
@@ -58,8 +52,8 @@ pub type Result<T> = result::Result<T, Error>;
/// # Arguments
///
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
pub fn setup_fpu(vcpu: &VcpuFd) -> Result<()> {
let fpu: kvm_fpu = kvm_fpu {
pub fn setup_fpu(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
let fpu: FpuState = FpuState {
fcw: 0x37f,
mxcsr: 0x1f80,
..Default::default()
@@ -73,8 +67,8 @@ pub fn setup_fpu(vcpu: &VcpuFd) -> Result<()> {
/// # Arguments
///
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
pub fn setup_msrs(vcpu: &VcpuFd) -> Result<()> {
vcpu.set_msrs(&boot_msr_entries())
pub fn setup_msrs(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
vcpu.set_msrs(&hypervisor::x86_64::boot_msr_entries())
.map_err(Error::SetModelSpecificRegisters)?;
Ok(())
@@ -89,22 +83,22 @@ pub fn setup_msrs(vcpu: &VcpuFd) -> Result<()> {
/// * `boot_sp` - Starting stack pointer.
/// * `boot_si` - Must point to zero page address per Linux ABI.
pub fn setup_regs(
vcpu: &VcpuFd,
vcpu: &Arc<dyn hypervisor::Vcpu>,
boot_ip: u64,
boot_sp: u64,
boot_si: u64,
boot_prot: BootProtocol,
) -> Result<()> {
let regs: kvm_regs = match boot_prot {
let regs: StandardRegisters = match boot_prot {
// Configure regs as required by PVH boot protocol.
BootProtocol::PvhBoot => kvm_regs {
BootProtocol::PvhBoot => StandardRegisters {
rflags: 0x0000000000000002u64,
rbx: PVH_INFO_START.raw_value(),
rip: boot_ip,
..Default::default()
},
// Configure regs as required by Linux 64-bit boot protocol.
BootProtocol::LinuxBoot => kvm_regs {
BootProtocol::LinuxBoot => StandardRegisters {
rflags: 0x0000000000000002u64,
rip: boot_ip,
rsp: boot_sp,
@@ -122,8 +116,12 @@ pub fn setup_regs(
///
/// * `mem` - The memory that will be passed to the guest.
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
pub fn setup_sregs(mem: &GuestMemoryMmap, vcpu: &VcpuFd, boot_prot: BootProtocol) -> Result<()> {
let mut sregs: kvm_sregs = vcpu.get_sregs().map_err(Error::GetStatusRegisters)?;
pub fn setup_sregs(
mem: &GuestMemoryMmap,
vcpu: &Arc<dyn hypervisor::Vcpu>,
boot_prot: BootProtocol,
) -> Result<()> {
let mut sregs: SpecialRegisters = vcpu.get_sregs().map_err(Error::GetStatusRegisters)?;
configure_segments_and_sregs(mem, &mut sregs, boot_prot)?;
@@ -162,9 +160,9 @@ fn write_idt_value(val: u64, guest_mem: &GuestMemoryMmap) -> Result<()> {
.map_err(Error::WriteIDT)
}
fn configure_segments_and_sregs(
pub fn configure_segments_and_sregs(
mem: &GuestMemoryMmap,
sregs: &mut kvm_sregs,
sregs: &mut SpecialRegisters,
boot_prot: BootProtocol,
) -> Result<()> {
let gdt_table: [u64; BOOT_GDT_MAX as usize] = match boot_prot {
@@ -188,9 +186,9 @@ fn configure_segments_and_sregs(
}
};
let code_seg = kvm_segment_from_gdt(gdt_table[1], 1);
let data_seg = kvm_segment_from_gdt(gdt_table[2], 2);
let tss_seg = kvm_segment_from_gdt(gdt_table[3], 3);
let code_seg = segment_from_gdt(gdt_table[1], 1);
let data_seg = segment_from_gdt(gdt_table[2], 2);
let tss_seg = segment_from_gdt(gdt_table[3], 3);
// Write segments
write_gdt_table(&gdt_table[..], mem)?;
@@ -224,9 +222,8 @@ fn configure_segments_and_sregs(
Ok(())
}
fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut kvm_sregs) -> Result<()> {
pub fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut SpecialRegisters) -> Result<()> {
// Puts PML5 or PML4 right after zero page but aligned to 4k.
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)
@@ -259,52 +256,11 @@ fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut kvm_sregs) -> Result<()>
Ok(())
}
macro_rules! kvm_msr {
($msr:expr) => {
kvm_msr_entry {
index: $msr,
data: 0x0,
..Default::default()
}
};
}
macro_rules! kvm_msr_data {
($msr:expr, $data:expr) => {
kvm_msr_entry {
index: $msr,
data: $data,
..Default::default()
}
};
}
pub fn boot_msr_entries() -> Msrs {
Msrs::from_entries(&[
kvm_msr!(msr_index::MSR_IA32_SYSENTER_CS),
kvm_msr!(msr_index::MSR_IA32_SYSENTER_ESP),
kvm_msr!(msr_index::MSR_IA32_SYSENTER_EIP),
kvm_msr!(msr_index::MSR_STAR),
kvm_msr!(msr_index::MSR_CSTAR),
kvm_msr!(msr_index::MSR_LSTAR),
kvm_msr!(msr_index::MSR_KERNEL_GS_BASE),
kvm_msr!(msr_index::MSR_SYSCALL_MASK),
kvm_msr!(msr_index::MSR_IA32_TSC),
kvm_msr_data!(
msr_index::MSR_IA32_MISC_ENABLE,
msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64
),
kvm_msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
])
}
#[cfg(test)]
mod tests {
extern crate kvm_ioctls;
extern crate vm_memory;
use super::*;
use kvm_ioctls::Kvm;
use vm_memory::{GuestAddress, GuestMemoryMmap};
fn create_guest_mem() -> GuestMemoryMmap {
@@ -317,7 +273,7 @@ mod tests {
#[test]
fn segments_and_sregs() {
let mut sregs: kvm_sregs = Default::default();
let mut sregs: SpecialRegisters = Default::default();
let gm = create_guest_mem();
configure_segments_and_sregs(&gm, &mut sregs, BootProtocol::LinuxBoot).unwrap();
@@ -381,7 +337,7 @@ mod tests {
#[test]
fn page_tables() {
let mut sregs: kvm_sregs = Default::default();
let mut sregs: SpecialRegisters = Default::default();
let gm = create_guest_mem();
setup_page_tables(&gm, &mut sregs).unwrap();
@@ -405,96 +361,4 @@ mod tests {
assert_eq!(X86_CR4_PAE, sregs.cr4);
assert_eq!(X86_CR0_PG, sregs.cr0);
}
#[test]
fn test_setup_fpu() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
setup_fpu(&vcpu).unwrap();
let expected_fpu: kvm_fpu = kvm_fpu {
fcw: 0x37f,
mxcsr: 0x1f80,
..Default::default()
};
let actual_fpu: kvm_fpu = vcpu.get_fpu().unwrap();
// TODO: auto-generate kvm related structures with PartialEq on.
assert_eq!(expected_fpu.fcw, actual_fpu.fcw);
// Setting the mxcsr register from kvm_fpu inside setup_fpu does not influence anything.
// See 'kvm_arch_vcpu_ioctl_set_fpu' from arch/x86/kvm/x86.c.
// The mxcsr will stay 0 and the assert below fails. Decide whether or not we should
// remove it at all.
// assert!(expected_fpu.mxcsr == actual_fpu.mxcsr);
}
#[test]
fn test_setup_msrs() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
setup_msrs(&vcpu).unwrap();
// This test will check against the last MSR entry configured (the tenth one).
// See create_msr_entries for details.
let mut msrs = Msrs::from_entries(&[kvm_msr_entry {
index: msr_index::MSR_IA32_MISC_ENABLE,
..Default::default()
}]);
// get_msrs returns the number of msrs that it succeed in reading. We only want to read 1
// in this test case scenario.
let read_msrs = vcpu.get_msrs(&mut msrs).unwrap();
assert_eq!(read_msrs, 1);
// Official entries that were setup when we did setup_msrs. We need to assert that the
// tenth one (i.e the one with index msr_index::MSR_IA32_MISC_ENABLE has the data we
// expect.
let entry_vec = boot_msr_entries();
assert_eq!(entry_vec.as_slice()[9], msrs.as_slice()[0]);
}
#[test]
fn test_setup_regs() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
let expected_regs: kvm_regs = kvm_regs {
rflags: 0x0000000000000002u64,
rip: 1,
rsp: 2,
rbp: 2,
rsi: 3,
..Default::default()
};
setup_regs(
&vcpu,
expected_regs.rip,
expected_regs.rsp,
expected_regs.rsi,
BootProtocol::LinuxBoot,
)
.unwrap();
let actual_regs: kvm_regs = vcpu.get_regs().unwrap();
assert_eq!(actual_regs, expected_regs);
}
#[test]
fn test_setup_sregs() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
let mut expected_sregs: kvm_sregs = vcpu.get_sregs().unwrap();
let gm = create_guest_mem();
configure_segments_and_sregs(&gm, &mut expected_sregs, BootProtocol::LinuxBoot).unwrap();
setup_page_tables(&gm, &mut expected_sregs).unwrap();
setup_sregs(&gm, &vcpu, BootProtocol::LinuxBoot).unwrap();
let actual_sregs: kvm_sregs = vcpu.get_sregs().unwrap();
assert_eq!(expected_sregs, actual_sregs);
}
}

263
arch/src/x86_64/smbios.rs Normal file
View File

@@ -0,0 +1,263 @@
// Copyright © 2020 Intel Corporation
//
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use layout::SMBIOS_START;
use std::fmt::{self, Display};
use std::mem;
use std::result;
use std::slice;
use vm_memory::ByteValued;
use vm_memory::{Address, Bytes, GuestAddress, GuestMemoryMmap};
#[allow(unused_variables)]
#[derive(Debug)]
pub enum Error {
/// There was too little guest memory to store the entire SMBIOS table.
NotEnoughMemory,
/// The SMBIOS table has too little address space to be stored.
AddressOverflow,
/// Failure while zeroing out the memory for the SMBIOS table.
Clear,
/// Failure to write SMBIOS entrypoint structure
WriteSmbiosEp,
/// Failure to write additional data to memory
WriteData,
}
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
let description = match self {
NotEnoughMemory => "There was too little guest memory to store the SMBIOS table",
AddressOverflow => "The SMBIOS table has too little address space to be stored",
Clear => "Failure while zeroing out the memory for the SMBIOS table",
WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure",
WriteData => "Failure to write additional data to memory",
};
write!(f, "SMBIOS error: {}", description)
}
}
pub type Result<T> = result::Result<T, Error>;
// Constants sourced from SMBIOS Spec 3.2.0.
const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
const BIOS_INFORMATION: u8 = 0;
const SYSTEM_INFORMATION: u8 = 1;
const END_OF_TABLE: u8 = 127;
const PCI_SUPPORTED: u64 = 1 << 7;
const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
fn compute_checksum<T: Copy>(v: &T) -> u8 {
// Safe because we are only reading the bytes within the size of the `T` reference `v`.
let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) };
let mut checksum: u8 = 0;
for i in v_slice.iter() {
checksum = checksum.wrapping_add(*i);
}
(!checksum).wrapping_add(1)
}
#[repr(packed)]
#[derive(Default, Copy)]
pub struct Smbios30Entrypoint {
pub signature: [u8; 5usize],
pub checksum: u8,
pub length: u8,
pub majorver: u8,
pub minorver: u8,
pub docrev: u8,
pub revision: u8,
pub reserved: u8,
pub max_size: u32,
pub physptr: u64,
}
unsafe impl ByteValued for Smbios30Entrypoint {}
impl Clone for Smbios30Entrypoint {
fn clone(&self) -> Self {
*self
}
}
#[repr(packed)]
#[derive(Default, Copy)]
pub struct SmbiosBiosInfo {
pub typ: u8,
pub length: u8,
pub handle: u16,
pub vendor: u8,
pub version: u8,
pub start_addr: u16,
pub release_date: u8,
pub rom_size: u8,
pub characteristics: u64,
pub characteristics_ext1: u8,
pub characteristics_ext2: u8,
}
impl Clone for SmbiosBiosInfo {
fn clone(&self) -> Self {
*self
}
}
unsafe impl ByteValued for SmbiosBiosInfo {}
#[repr(packed)]
#[derive(Default, Copy)]
pub struct SmbiosSysInfo {
pub typ: u8,
pub length: u8,
pub handle: u16,
pub manufacturer: u8,
pub product_name: u8,
pub version: u8,
pub serial_number: u8,
pub uuid: [u8; 16usize],
pub wake_up_type: u8,
pub sku: u8,
pub family: u8,
}
impl Clone for SmbiosSysInfo {
fn clone(&self) -> Self {
*self
}
}
unsafe impl ByteValued for SmbiosSysInfo {}
fn write_and_incr<T: ByteValued>(
mem: &GuestMemoryMmap,
val: T,
mut curptr: GuestAddress,
) -> Result<GuestAddress> {
mem.write_obj(val, curptr).map_err(|_| Error::WriteData)?;
curptr = curptr
.checked_add(mem::size_of::<T>() as u64)
.ok_or(Error::NotEnoughMemory)?;
Ok(curptr)
}
fn write_string(
mem: &GuestMemoryMmap,
val: &str,
mut curptr: GuestAddress,
) -> Result<GuestAddress> {
for c in val.as_bytes().iter() {
curptr = write_and_incr(mem, *c, curptr)?;
}
curptr = write_and_incr(mem, 0 as u8, curptr)?;
Ok(curptr)
}
pub fn setup_smbios(mem: &GuestMemoryMmap) -> Result<()> {
let physptr = GuestAddress(SMBIOS_START)
.checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
.ok_or(Error::NotEnoughMemory)?;
let mut curptr = physptr;
let mut handle = 0;
{
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;
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)?;
}
{
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
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)?;
}
{
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;
curptr = write_and_incr(mem, smbios_sysinfo, curptr)?;
curptr = write_and_incr(mem, 0 as u8, 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;
smbios_ep.checksum = compute_checksum(&smbios_ep);
mem.write_obj(smbios_ep, GuestAddress(SMBIOS_START))
.map_err(|_| Error::WriteSmbiosEp)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn struct_size() {
assert_eq!(
mem::size_of::<Smbios30Entrypoint>(),
0x18usize,
concat!("Size of: ", stringify!(Smbios30Entrypoint))
);
assert_eq!(
mem::size_of::<SmbiosBiosInfo>(),
0x14usize,
concat!("Size of: ", stringify!(SmbiosBiosInfo))
);
assert_eq!(
mem::size_of::<SmbiosSysInfo>(),
0x1busize,
concat!("Size of: ", stringify!(SmbiosSysInfo))
);
}
#[test]
fn entrypoint_checksum() {
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(SMBIOS_START), 4096)]).unwrap();
setup_smbios(&mem).unwrap();
let smbios_ep: Smbios30Entrypoint = mem.read_obj(GuestAddress(SMBIOS_START)).unwrap();
assert_eq!(compute_checksum(&smbios_ep), 0);
}
}

View File

@@ -20,6 +20,3 @@
#[allow(non_upper_case_globals)]
#[allow(clippy::unreadable_literal, clippy::redundant_static_lifetimes)]
pub mod mpspec;
#[allow(non_upper_case_globals)]
#[allow(clippy::unreadable_literal, clippy::redundant_static_lifetimes)]
pub mod msr_index;

21
block_util/Cargo.toml Normal file
View File

@@ -0,0 +1,21 @@
[package]
name = "block_util"
version = "0.1.0"
authors = ["The Cloud Hypervisor Authors"]
edition = "2018"
[features]
default = []
io_uring = []
[dependencies]
io-uring = { git = "https://github.com/tokio-rs/io-uring.git", branch = "0.4" }
libc = "0.2.74"
log = "0.4.11"
serde = ">=1.0.27"
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
vm-memory = { version = "0.2.1", features = ["backend-mmap", "backend-atomic"] }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = ">=0.3.1"

545
block_util/src/lib.rs Normal file
View File

@@ -0,0 +1,545 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "io_uring")]
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::os::linux::fs::MetadataExt;
#[cfg(feature = "io_uring")]
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::PathBuf;
use std::result;
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;
#[derive(Debug)]
pub enum Error {
/// Guest gave us bad memory addresses.
GuestMemory(GuestMemoryError),
/// Guest gave us offsets that would have overflowed a usize.
CheckedOffset(GuestAddress, usize),
/// Guest gave us a write only descriptor that protocol says to read from.
UnexpectedWriteOnlyDescriptor,
/// Guest gave us a read only descriptor that protocol says to write to.
UnexpectedReadOnlyDescriptor,
/// Guest gave us too few descriptors in a descriptor chain.
DescriptorChainTooShort,
/// Guest gave us a descriptor that was too short to use.
DescriptorLengthTooSmall,
/// Getting a block's metadata fails for any reason.
GetFileMetadata,
/// The requested operation would cause a seek beyond disk end.
InvalidOffset,
}
fn build_device_id(disk_path: &PathBuf) -> result::Result<String, Error> {
let blk_metadata = match disk_path.metadata() {
Err(_) => return Err(Error::GetFileMetadata),
Ok(m) => m,
};
// This is how kvmtool does it.
let device_id = format!(
"{}{}{}",
blk_metadata.st_dev(),
blk_metadata.st_rdev(),
blk_metadata.st_ino()
);
Ok(device_id)
}
pub fn build_disk_image_id(disk_path: &PathBuf) -> Vec<u8> {
let mut default_disk_image_id = vec![0; VIRTIO_BLK_ID_BYTES as usize];
match build_device_id(disk_path) {
Err(_) => {
warn!("Could not generate device id. We'll use a default.");
}
Ok(m) => {
// The kernel only knows to read a maximum of VIRTIO_BLK_ID_BYTES.
// This will also zero out any leftover bytes.
let disk_id = m.as_bytes();
let bytes_to_copy = cmp::min(disk_id.len(), VIRTIO_BLK_ID_BYTES as usize);
default_disk_image_id[..bytes_to_copy].clone_from_slice(&disk_id[..bytes_to_copy])
}
}
default_disk_image_id
}
#[derive(Debug)]
pub enum ExecuteError {
BadRequest(Error),
Flush(io::Error),
Read(GuestMemoryError),
Seek(io::Error),
Write(GuestMemoryError),
Unsupported(u32),
SubmitIoUring(io::Error),
GetHostAddress(GuestMemoryError),
}
impl ExecuteError {
pub fn status(&self) -> u32 {
match *self {
ExecuteError::BadRequest(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Read(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Seek(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Write(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Unsupported(_) => VIRTIO_BLK_S_UNSUPP,
ExecuteError::SubmitIoUring(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::GetHostAddress(_) => VIRTIO_BLK_S_IOERR,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RequestType {
In,
Out,
Flush,
GetDeviceID,
Unsupported(u32),
}
pub fn request_type(
mem: &GuestMemoryMmap,
desc_addr: GuestAddress,
) -> result::Result<RequestType, Error> {
let type_ = mem.read_obj(desc_addr).map_err(Error::GuestMemory)?;
match 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),
t => Ok(RequestType::Unsupported(t)),
}
}
fn sector(mem: &GuestMemoryMmap, desc_addr: GuestAddress) -> result::Result<u64, Error> {
const SECTOR_OFFSET: usize = 8;
let addr = match mem.checked_offset(desc_addr, SECTOR_OFFSET) {
Some(v) => v,
None => return Err(Error::CheckedOffset(desc_addr, SECTOR_OFFSET)),
};
mem.read_obj(addr).map_err(Error::GuestMemory)
}
pub struct Request {
pub request_type: RequestType,
pub sector: u64,
pub data_addr: GuestAddress,
pub data_len: u32,
pub status_addr: GuestAddress,
pub writeback: bool,
}
impl Request {
pub fn parse(
avail_desc: &DescriptorChain,
mem: &GuestMemoryMmap,
) -> result::Result<Request, Error> {
// The head contains the request type which MUST be readable.
if avail_desc.is_write_only() {
return Err(Error::UnexpectedWriteOnlyDescriptor);
}
let mut req = Request {
request_type: request_type(&mem, avail_desc.addr)?,
sector: sector(&mem, avail_desc.addr)?,
data_addr: GuestAddress(0),
data_len: 0,
status_addr: GuestAddress(0),
writeback: true,
};
let data_desc;
let status_desc;
let desc = avail_desc
.next_descriptor()
.ok_or(Error::DescriptorChainTooShort)?;
if !desc.has_next() {
status_desc = desc;
// Only flush requests are allowed to skip the data descriptor.
if req.request_type != RequestType::Flush {
return Err(Error::DescriptorChainTooShort);
}
} else {
data_desc = desc;
status_desc = data_desc
.next_descriptor()
.ok_or(Error::DescriptorChainTooShort)?;
if data_desc.is_write_only() && req.request_type == RequestType::Out {
return Err(Error::UnexpectedWriteOnlyDescriptor);
}
if !data_desc.is_write_only() && req.request_type == RequestType::In {
return Err(Error::UnexpectedReadOnlyDescriptor);
}
if !data_desc.is_write_only() && req.request_type == RequestType::GetDeviceID {
return Err(Error::UnexpectedReadOnlyDescriptor);
}
req.data_addr = data_desc.addr;
req.data_len = data_desc.len;
}
// The status MUST always be writable.
if !status_desc.is_write_only() {
return Err(Error::UnexpectedReadOnlyDescriptor);
}
if status_desc.len < 1 {
return Err(Error::DescriptorLengthTooSmall);
}
req.status_addr = status_desc.addr;
Ok(req)
}
#[allow(clippy::ptr_arg)]
pub fn execute<T: Seek + Read + Write>(
&self,
disk: &mut T,
disk_nsectors: u64,
mem: &GuestMemoryMmap,
disk_id: &Vec<u8>,
) -> result::Result<u32, ExecuteError> {
let mut top: u64 = u64::from(self.data_len) / SECTOR_SIZE;
if u64::from(self.data_len) % SECTOR_SIZE != 0 {
top += 1;
}
top = top
.checked_add(self.sector)
.ok_or(ExecuteError::BadRequest(Error::InvalidOffset))?;
if top > disk_nsectors {
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
}
disk.seek(SeekFrom::Start(self.sector << SECTOR_SHIFT))
.map_err(ExecuteError::Seek)?;
match self.request_type {
RequestType::In => {
mem.read_exact_from(self.data_addr, disk, self.data_len as usize)
.map_err(ExecuteError::Read)?;
return Ok(self.data_len);
}
RequestType::Out => {
mem.write_all_to(self.data_addr, disk, self.data_len as usize)
.map_err(ExecuteError::Write)?;
if !self.writeback {
disk.flush().map_err(ExecuteError::Flush)?;
}
}
RequestType::Flush => disk.flush().map_err(ExecuteError::Flush)?,
RequestType::GetDeviceID => {
if (self.data_len as usize) < disk_id.len() {
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
}
mem.write_slice(&disk_id.as_slice(), self.data_addr)
.map_err(ExecuteError::Write)?;
}
RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
};
Ok(0)
}
#[cfg(feature = "io_uring")]
pub fn execute_io_uring(
&self,
mem: &GuestMemoryMmap,
io_uring: &mut IoUring,
disk_nsectors: u64,
disk_image_fd: RawFd,
disk_id: &[u8],
user_data: u64,
) -> result::Result<bool, ExecuteError> {
let data_len = self.data_len;
let sector = self.sector;
let data_addr = self.data_addr;
let request_type = self.request_type;
let mut top: u64 = u64::from(data_len) / SECTOR_SIZE;
if u64::from(data_len) % SECTOR_SIZE != 0 {
top += 1;
}
top = top
.checked_add(sector)
.ok_or(ExecuteError::BadRequest(Error::InvalidOffset))?;
if top > disk_nsectors {
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
}
let buf = mem
.get_slice(data_addr, data_len as usize)
.map_err(ExecuteError::GetHostAddress)?
.as_ptr();
let offset = (sector as i64) << SECTOR_SHIFT;
let (submitter, sq, _) = io_uring.split();
let mut avail_sq = sq.available();
// 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::Read::new(opcode::types::Fd(disk_image_fd), buf, data_len)
.offset(offset)
.build()
.user_data(user_data),
)
};
}
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::Write::new(opcode::types::Fd(disk_image_fd), buf, data_len)
.offset(offset)
.build()
.user_data(user_data),
)
};
}
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()
.user_data(user_data),
)
};
}
RequestType::GetDeviceID => {
if (data_len as usize) < disk_id.len() {
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
}
mem.write_slice(disk_id, data_addr)
.map_err(ExecuteError::Write)?;
return Ok(false);
}
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)
}
pub fn set_writeback(&mut self, writeback: bool) {
self.writeback = writeback
}
}
#[derive(Copy, Clone, Debug, Default, Deserialize)]
#[repr(C, packed)]
pub struct VirtioBlockConfig {
pub capacity: u64,
pub size_max: u32,
pub seg_max: u32,
pub geometry: VirtioBlockGeometry,
pub blk_size: u32,
pub physical_block_exp: u8,
pub alignment_offset: u8,
pub min_io_size: u16,
pub opt_io_size: u32,
pub writeback: u8,
pub unused: u8,
pub num_queues: u16,
pub max_discard_sectors: u32,
pub max_discard_seg: u32,
pub discard_sector_alignment: u32,
pub max_write_zeroes_sectors: u32,
pub max_write_zeroes_seg: u32,
pub write_zeroes_may_unmap: u8,
pub unused1: [u8; 3],
}
// We must explicitly implement Serialize since the structure is packed and
// it's unsafe to borrow from a packed structure. And by default, if we derive
// Serialize from serde, it will borrow the values from the structure.
// That's why this implementation copies each field separately before it
// serializes the entire structure field by field.
impl Serialize for VirtioBlockConfig {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let capacity = self.capacity;
let size_max = self.size_max;
let seg_max = self.seg_max;
let geometry = self.geometry;
let blk_size = self.blk_size;
let physical_block_exp = self.physical_block_exp;
let alignment_offset = self.alignment_offset;
let min_io_size = self.min_io_size;
let opt_io_size = self.opt_io_size;
let writeback = self.writeback;
let unused = self.unused;
let num_queues = self.num_queues;
let max_discard_sectors = self.max_discard_sectors;
let max_discard_seg = self.max_discard_seg;
let discard_sector_alignment = self.discard_sector_alignment;
let max_write_zeroes_sectors = self.max_write_zeroes_sectors;
let max_write_zeroes_seg = self.max_write_zeroes_seg;
let write_zeroes_may_unmap = self.write_zeroes_may_unmap;
let unused1 = self.unused1;
let mut virtio_block_config = serializer.serialize_struct("VirtioBlockConfig", 60)?;
virtio_block_config.serialize_field("capacity", &capacity)?;
virtio_block_config.serialize_field("size_max", &size_max)?;
virtio_block_config.serialize_field("seg_max", &seg_max)?;
virtio_block_config.serialize_field("geometry", &geometry)?;
virtio_block_config.serialize_field("blk_size", &blk_size)?;
virtio_block_config.serialize_field("physical_block_exp", &physical_block_exp)?;
virtio_block_config.serialize_field("alignment_offset", &alignment_offset)?;
virtio_block_config.serialize_field("min_io_size", &min_io_size)?;
virtio_block_config.serialize_field("opt_io_size", &opt_io_size)?;
virtio_block_config.serialize_field("writeback", &writeback)?;
virtio_block_config.serialize_field("unused", &unused)?;
virtio_block_config.serialize_field("num_queues", &num_queues)?;
virtio_block_config.serialize_field("max_discard_sectors", &max_discard_sectors)?;
virtio_block_config.serialize_field("max_discard_seg", &max_discard_seg)?;
virtio_block_config
.serialize_field("discard_sector_alignment", &discard_sector_alignment)?;
virtio_block_config
.serialize_field("max_write_zeroes_sectors", &max_write_zeroes_sectors)?;
virtio_block_config.serialize_field("max_write_zeroes_seg", &max_write_zeroes_seg)?;
virtio_block_config.serialize_field("write_zeroes_may_unmap", &write_zeroes_may_unmap)?;
virtio_block_config.serialize_field("unused1", &unused1)?;
virtio_block_config.end()
}
}
unsafe impl ByteValued for VirtioBlockConfig {}
#[derive(Copy, Clone, Debug, Default, Deserialize)]
#[repr(C, packed)]
pub struct VirtioBlockGeometry {
pub cylinders: u16,
pub heads: u8,
pub sectors: u8,
}
// We must explicitly implement Serialize since the structure is packed and
// it's unsafe to borrow from a packed structure. And by default, if we derive
// Serialize from serde, it will borrow the values from the structure.
// That's why this implementation copies each field separately before it
// serializes the entire structure field by field.
impl Serialize for VirtioBlockGeometry {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let cylinders = self.cylinders;
let heads = self.heads;
let sectors = self.sectors;
let mut virtio_block_geometry = serializer.serialize_struct("VirtioBlockGeometry", 4)?;
virtio_block_geometry.serialize_field("cylinders", &cylinders)?;
virtio_block_geometry.serialize_field("heads", &heads)?;
virtio_block_geometry.serialize_field("sectors", &sectors)?;
virtio_block_geometry.end()
}
}
unsafe impl ByteValued for VirtioBlockGeometry {}
/// Check if io_uring for block device can be used on the current system, as
/// it correctly supports the expected io_uring features.
#[cfg(feature = "io_uring")]
pub fn block_io_uring_is_supported() -> bool {
let error_msg = "io_uring not supported:";
// Check we can create an io_uring instance, which effectively verifies
// that io_uring_setup() syscall is supported.
let io_uring = match IoUring::new(1) {
Ok(io_uring) => io_uring,
Err(e) => {
info!("{} failed to create io_uring instance: {}", error_msg, e);
return false;
}
};
let submitter = io_uring.submitter();
let event_fd = match EventFd::new(libc::EFD_NONBLOCK) {
Ok(fd) => fd,
Err(e) => {
info!("{} failed to create eventfd: {}", error_msg, e);
return false;
}
};
// Check we can register an eventfd as this is going to be needed while
// using io_uring with the virtio block device. This also validates that
// io_uring_register() syscall is supported.
match submitter.register_eventfd(event_fd.as_raw_fd()) {
Ok(_) => {}
Err(e) => {
info!("{} failed to register eventfd: {}", error_msg, e);
return false;
}
}
let mut probe = Probe::new();
// Check we can register a probe to validate supported operations.
match submitter.register_probe(&mut probe) {
Ok(_) => {}
Err(e) => {
info!("{} failed to register a probe: {}", error_msg, e);
return false;
}
}
// Check IORING_OP_FSYNC is supported
if !probe.is_supported(opcode::Fsync::CODE) {
info!("{} IORING_OP_FSYNC operation not supported", error_msg);
return false;
}
// Check IORING_OP_READ is supported
if !probe.is_supported(opcode::Read::CODE) {
info!("{} IORING_OP_READ operation not supported", error_msg);
return false;
}
// Check IORING_OP_WRITE is supported
if !probe.is_supported(opcode::Write::CODE) {
info!("{} IORING_OP_WRITE operation not supported", error_msg);
return false;
}
true
}

View File

@@ -8,8 +8,8 @@ anyhow = "1.0"
bitflags = ">=1.2.1"
byteorder = "1.3.4"
epoll = ">=4.0.1"
libc = "0.2.71"
log = "0.4.8"
libc = "0.2.74"
log = "0.4.11"
serde = {version = ">=1.0.27", features = ["rc"] }
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"

View File

@@ -5,6 +5,7 @@
use acpi_tables::{aml, aml::Aml};
use std::sync::Arc;
use std::time::Instant;
use vm_device::interrupt::InterruptSourceGroup;
use vmm_sys_util::eventfd::EventFd;
use BusDevice;
@@ -149,3 +150,39 @@ impl Aml for AcpiGEDDevice {
.to_aml_bytes()
}
}
pub struct AcpiPMTimerDevice {
start: Instant,
}
impl AcpiPMTimerDevice {
pub fn new() -> Self {
Self {
start: Instant::now(),
}
}
}
impl Default for AcpiPMTimerDevice {
fn default() -> Self {
Self::new()
}
}
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);
let nanos = since.as_nanos();
const PM_TIMER_FREQUENCY_HZ: u128 = 3_579_545;
const NANOS_PER_SECOND: u128 = 1_000_000_000;
let counter = (nanos * PM_TIMER_FREQUENCY_HZ) / NANOS_PER_SECOND;
let counter: u32 = (counter & 0xffff_ffff) as u32;
data.copy_from_slice(&counter.to_le_bytes());
}
fn write(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
}

View File

@@ -344,6 +344,7 @@ impl Ioapic {
high_addr: 0x0,
low_addr,
data,
devid: 0,
};
self.interrupt_source_group

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use libc::{gmtime_r, time, time_t, tm};
use libc::{clock_gettime, gmtime_r, time_t, timespec, tm, CLOCK_REALTIME};
use std::cmp::min;
use std::mem;
@@ -77,14 +77,17 @@ impl BusDevice for Cmos {
let day;
let month;
let year;
// The time and gmtime_r calls are safe as long as the structs they are given are
// large enough, and neither of them fail. It is safe to zero initialize the tm
// struct because it contains only plain data.
unsafe {
// The clock_gettime and gmtime_r calls are safe as long as the structs they are
// given are large enough, and neither of them fail. It is safe to zero initialize
// the tm and timespec struct because it contains only plain data.
let update_in_progress = unsafe {
let mut timespec: timespec = mem::zeroed();
clock_gettime(CLOCK_REALTIME, &mut timespec as *mut _);
let now: time_t = timespec.tv_sec;
let mut tm: tm = mem::zeroed();
let mut now: time_t = 0;
time(&mut now as *mut _);
gmtime_r(&now, &mut tm as *mut _);
// The following lines of code are safe but depend on tm being in scope.
seconds = tm.tm_sec;
minutes = tm.tm_min;
@@ -93,6 +96,11 @@ impl BusDevice for Cmos {
day = tm.tm_mday;
month = tm.tm_mon + 1;
year = tm.tm_year;
// Update in Progress bit held for last 224us of each second
const NANOSECONDS_PER_SECOND: i64 = 1_000_000_000;
const UIP_HOLD_LENGTH: i64 = 8 * NANOSECONDS_PER_SECOND / 32768;
timespec.tv_nsec >= (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH)
};
match self.index {
0x00 => to_bcd(seconds as u8),
@@ -102,6 +110,8 @@ impl BusDevice for Cmos {
0x07 => to_bcd(day as u8),
0x08 => to_bcd(month as u8),
0x09 => to_bcd((year % 100) as u8),
// Bit 5 for 32kHz clock. Bit 7 for Update in Progress
0x0a => 1 << 5 | (update_in_progress as u8) << 7,
0x32 => to_bcd(((year + 1900) / 100) as u8),
_ => {
// self.index is always guaranteed to be in range via INDEX_MASK.

View File

@@ -39,7 +39,7 @@ pub mod ioapic;
pub mod legacy;
#[cfg(feature = "acpi")]
pub use self::acpi::{AcpiGEDDevice, AcpiShutdownDevice};
pub use self::acpi::{AcpiGEDDevice, AcpiPMTimerDevice, AcpiShutdownDevice};
pub use self::bus::{Bus, BusDevice, Error as BusError};
pub type DeviceEventT = u16;

View File

@@ -78,25 +78,26 @@ Shut the VMM down | `/vmm.shutdown` | N/A | N/A
#### Virtual Machine (VM) Actions
Action | Endpoint | Request Body | Response Body | Prerequisites
-----------------------------------|---------------------|---------------------------|-------------------|---------------------------
Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet
Delete the VM | `/vm.delete` | N/A | N/A | N/A
Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted
Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted
Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted
Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted
Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused
Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
Remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created
Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | N/A | The VM is booted
Remove VFIO PCI device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted
Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | N/A | The VM is booted
Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | N/A | The VM is booted
Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | N/A | The VM is booted
Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | N/A | The VM is booted
Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | N/A | The VM is booted
Action | Endpoint | Request Body | Response Body | Prerequisites
-----------------------------------|---------------------|---------------------------|--------------------------|---------------------------
Create the VM | `/vm.create` | `/schemas/VmConfig` | N/A | The VM is not created yet
Delete the VM | `/vm.delete` | N/A | N/A | N/A
Boot the VM | `/vm.boot` | N/A | N/A | The VM is created but not booted
Shut the VM down | `/vm.shutdown` | N/A | N/A | The VM is booted
Reboot the VM | `/vm.reboot` | N/A | N/A | The VM is booted
Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted
Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused
Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
Remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted
Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created
Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted
Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted
Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | `/schemas/PciDeviceInfo` | The VM is booted
Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | `/schemas/PciDeviceInfo` | The VM is booted
Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | `/schemas/PciDeviceInfo` | The VM is booted
Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted
Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted
Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted
### REST API Examples
@@ -121,10 +122,10 @@ We want to create a virtual machine with the following characteristics:
* 4 vCPUs
* 1 GB of RAM
* 1 virtio based networking interface
* Direct kernel boot from a custom 5.5.0 Linux kernel located at
* Direct kernel boot from a custom 5.6.0-rc4 Linux kernel located at
`/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu`
* Using a Clear Linux image as its root filesystem, located at
`/opt/clh/images/clear-30080-kvm.img`
* Using a Ubuntu image as its root filesystem, located at
`/opt/clh/images/focal-server-cloudimg-amd64.raw`
```shell
#!/bin/bash
@@ -136,8 +137,8 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
-d '{
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
"kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
"cmdline":{"args":"console=hvc0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda3"},
"disks":[{"path":"/opt/clh/images/clear-30080-kvm.img"}],
"cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
"rng":{"src":"/dev/urandom"},
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
}'
@@ -304,8 +305,8 @@ APIs work together, let's look at a complete VM creation flow, from the
-d '{
"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
"kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
"cmdline":{"args":"console=hvc0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda3"},
"disks":[{"path":"/opt/clh/images/clear-30080-kvm.img"}],
"cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
"rng":{"src":"/dev/urandom"},
"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
}'

View File

@@ -1,11 +1,11 @@
# How to build and run Cloud-hypervisor on Arm64
# How to build and run Cloud-hypervisor on AArch64
Cloud-hypervisor is partially enabled on AArch64 architecture.
Although all features are not ready yet, you can begin to test Cloud-hypervisor on a Arm64 host by following this guide.
Although all features are not ready yet, you can begin to test Cloud-hypervisor on a AArch64 host by following this guide.
## Prerequisites
On Arm64 machines, Cloud-hypervisor depends on an external library `libfdt-dev` for generating Flatted Device Tree (FDT).
On AArch64 machines, Cloud-hypervisor depends on an external library `libfdt-dev` for generating Flattened Device Tree (FDT).
The long-term plan is to replace `libfdt-dev` with some pure-Rust component to get rid of such dependency.
@@ -16,16 +16,20 @@ sudo apt-get install libfdt-dev
## Build
Before building, a hack trick need to be performed to get rid of some build error in vmm component. See [this](https://github.com/cloud-hypervisor/kvm-bindings/pull/1) for more info about this temporary workaround.
For Virtio devices, you can choose MMIO or PCI as transport option.
### MMIO
```bash
sed -i 's/"with-serde",\ //g' vmm/Cargo.toml
cargo build --no-default-features --features mmio,kvm
```
The support of AArch64 is in very early stage, only Virtio devices with MMIO tranport is available.
### PCI
Using PCI devices requires GICv3-ITS for MSI messaging. GICv3-ITS is very common in modern servers, but your machine happen to be old ones with GICv2(M) (like Raspberry Pi 4) or GICv3 without ITS, MMIO can still work.
```bash
cargo build --no-default-features --features "mmio"
cargo build --no-default-features --features pci,kvm
```
## Image
@@ -33,7 +37,7 @@ cargo build --no-default-features --features "mmio"
Download kernel binary and rootfs image from AWS.
```bash
wget https://s3.amazonaws.com/spec.ccfc.min/img/aarch64/ubuntu_with_ssh/fsfiles/xenial.rootfs.ext4 -O rootfs.img
wget https://s3.amazonaws.com/spec.ccfc.min/img/aarch64/ubuntu_with_ssh/fsfiles/xenial.rootfs.ext4 -O rootfs.ext4
wget https://s3.amazonaws.com/spec.ccfc.min/img/aarch64/ubuntu_with_ssh/kernel/vmlinux.bin -O kernel.bin
```
@@ -47,7 +51,7 @@ To build the development container:
./scripts/dev_cli.sh build-container
```
To build Cloud-hypervisor in the container:
To build Cloud-hypervisor in the container: (The default option for Virtio transport is MMIO.)
```bash
./scripts/dev_cli.sh build
@@ -58,7 +62,7 @@ To build Cloud-hypervisor in the container:
Assuming you have built Cloud-hypervisor with the development container, a VM can be started with command:
```bash
sudo target/debug/cloud-hypervisor --kernel kernel.bin --disk path=rootfs.ext4 --cmdline "keep_bootcon console=hvc0 reboot=k panic=1 pci=off root=/dev/vda rw" --cpus boot=4 --memory size=512M --seccomp false --serial file=serial.log --log-file log.log -vvv
sudo build/cargo_target/aarch64-unknown-linux-gnu/debug/cloud-hypervisor --kernel kernel.bin --disk path=rootfs.ext4 --cmdline "keep_bootcon console=hvc0 reboot=k panic=1 pci=off root=/dev/vda rw" --cpus boot=4 --memory size=512M --serial file=serial.log --log-file log.log -vvv
```
If the build was done out of the container, replace the binary path with `build/cargo_target/aarch64-unknown-linux-gnu/debug/cloud-hypervisor`.
If the build was done out of the container, replace the binary path with `target/debug/cloud-hypervisor`.

View File

@@ -1,96 +1,136 @@
# How to create a custom Clear Linux image
# How to create a custom Ubuntu image
In the context of adding more utility to the cloudguest image being used
for integration testing, this is a quick guide on how to achieve the creation
of your own Clear Linux image using the official Clear Linux tooling.
## Prepare the environment
From the host, the goal is run a Clear Linux VM that will allow us to build
the custom image we want.
```bash
# Get latest CL version:
IMG_VERSION=$(curl https://download.clearlinux.org/latest)
# Get latest clear-kvm image:
wget -P $HOME/workloads/ https://download.clearlinux.org/current/clear-${IMG_VERSION}-kvm.img.xz
# Extract the image
unxz $HOME/workloads/clear-${IMG_VERSION}-kvm.img.xz
# Make sure cloud-hypervisor binary has CAP_NET_ADMIN capability set
sudo setcap cap_net_admin+ep cloud-hypervisor
# Boot cloud-hypervisor VM with the downloaded image
./cloud-hypervisor -v --kernel $HOME/workloads/vmlinux --disk path=clear-${IMG_VERSION}-kvm.img --cmdline "console=ttyS0 console=hvc0 reboot=k panic=1 nomodules root=/dev/vda3 rw" --cpus 1 --memory size=4G --net tap=,mac=
# Setup connectivity
# First make sure to enable IP forwarding (disabled on Linux by default)
sudo bash -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
# Retrieve the interface name and the gateway IP
IFACE=$(ip route | grep default | awk -F 'dev' '{print $2}' | awk -F ' ' '{print $1}')
GW=$(ip route | grep vmtap0 | awk -F ' ' '{print $1}')
# Create a new masquerade rule to tag the packets going out
sudo iptables -t nat -A POSTROUTING -s ${GW} -o ${IFACE} -j MASQUERADE
```
In the context of adding more utilities to the Ubuntu cloud image being used
for integration testing, this quick guide details how to achieve the proper
modification of an official Ubuntu cloud image.
## Create the image
From the guest, we can now create the image.
Let's go through the steps on how to extend an official Ubuntu image. These
steps can be applied to other distributions (with a few changes regarding
package management).
### Get latest Ubuntu cloud image
```bash
# Setup connectivity
sudo ip addr add 192.168.249.2/24 dev enp0s3
sudo ip route add default via 192.168.249.1
# Install necessary bundles
sudo swupd bundle-add clr-installer
sudo swupd bundle-add os-installer
# Download and update cloudguest image configuration
wget https://download.clearlinux.org/current/config/image/cloudguest.yaml
sed -i '/size: \"864M\"/d' cloudguest.yaml
sed -i 's/\"800M\"/\"2G\"/g' cloudguest.yaml
sed -i 's/bootloader,/bootloader,\n curl,\n iperf,/g' cloudguest.yaml
sed -i 's/systemd-networkd-autostart/sysadmin-basic,\n systemd-networkd-autostart/g' cloudguest.yaml
# Create the custom cloudguest image
clr-installer -c cloudguest.yaml
# Make the guest accessible through ssh
sudo mkdir -p /etc/ssh
sudo bash -c "echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config"
wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
```
### Retrieve the image
Once the new image has been created and the guest is accessible through
`ssh`, it is time to retrieve the image from the host.
### Check the file format is QCOW2
```bash
# Retrieve new image (this is a raw image)
scp root@192.168.249.2:cloudguest.img .
mv cloudguest.img clear-cloudguest-raw.img
# Create the QCOW image from the RAW image
qemu-img convert -p -f raw -O qcow2 clear-cloudguest-raw.img clear-cloudguest.img
# Compress the QCOW image
xz -k -T $(nproc) clear-cloudguest.img
file focal-server-cloudimg-amd64.img
focal-server-cloudimg-amd64.img: QEMU QCOW2 Image (v2), 2361393152 bytes
```
### Convert QCOW2 into RAW
```bash
qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
```
### Identify the Linux partition
The goal is to mount the image rootfs so that it can be modified as needed.
That's why we need to identify where the Linux filesystem partition is located
in the image.
```bash
sudo fdisk -l focal-server-cloudimg-amd64.raw
Disk focal-server-cloudimg-amd64.raw: 2.2 GiB, 2361393152 bytes, 4612096 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: A1171ABA-2BEA-4218-A467-1B2B607E5953
Device Start End Sectors Size Type
focal-server-cloudimg-amd64.raw1 227328 4612062 4384735 2.1G Linux filesystem
focal-server-cloudimg-amd64.raw14 2048 10239 8192 4M BIOS boot
focal-server-cloudimg-amd64.raw15 10240 227327 217088 106M EFI System
Partition table entries are not in disk order.
```
### Mount the Linux partition
```bash
mkdir -p /mnt
sudo mount -o loop,offset=$((227328 * 512)) focal-server-cloudimg-amd64.raw /mnt
```
### Change root directory
Changing the root directory will allow us to install new packages to the rootfs
contained by the cloud image.
```bash
sudo chroot /mnt
mount -t proc proc /proc
mount -t devpts devpts /dev/pts
```
### Install needed packages
In the context Cloud-Hypervisor's integration tests, we need several utilities.
Here is the way to install them for a Ubuntu image. This step is specific to
Ubuntu distributions.
```bash
apt update
apt install fio iperf iperf3 socat
```
### Remove 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.
```bash
apt remove --purge snapd
```
### Cleanup the image
Leave no trace in the image before unmounting its content.
```bash
umount /dev/pts
umount /proc
history -c
exit
umount /mnt
```
### Rename the image
Renaming is important to identify this is a modified image.
```bash
mv focal-server-cloudimg-amd64.raw focal-server-cloudimg-amd64-custom.raw
```
### 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
```
## Switch CI to use the new image
### Upload to Azure storage
The next step is to update the image stored as part of the Azure storage
account, replacing it with the newly created image. This will make this
new image available from the integration tests.
This is usually achieved through the web interface.
The next step is to update both images (QCOW2 and RAW) stored as part of the
Azure storage account, replacing them with the newly created ones. This will
make these new images available from the integration tests. This is usually
achieved through the web interface.
### Update integration tests
Last step is about updating the integration tests to work with this new image.
The key point is to identify the UUID of this new image so that it can be used
directly from the tests.
Proceed as follow to determine this UUID:
```bash
# Mount the image
sudo mount -o loop,offset=$((2048 * 512)) clear-cloudguest-raw.img /mnt/
# Identify UUID
sudo cat /mnt/loader/entries/Clear-linux-kvm-*.conf | grep "root=PARTUUID="
# Unmount the image
sudo umount /mnt
```
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.

View File

@@ -43,7 +43,7 @@ to easily grep for the tracing logs (e.g.
```
./target/debug/cloud-hypervisor \
--kernel ~/rust-hypervisor-firmware/target/target/release/hypervisor-fw \
--disk path=~/hypervisor/images/clear-30080-kvm.img \
--disk path=~/hypervisor/images/focal-server-cloudimg-amd64.raw \
--cpus 4 \
--memory size=1024M \
--rng \

View File

@@ -52,14 +52,14 @@ Direct kernel boot option is preferred since we need to provide the custom kerne
Because _vhost-user_ expects a dedicated process (__virtiofsd__ in this case) to be able to access the guest RAM to communicate through the _virtqueues_ with the driver running in the guest, `--memory` option needs to be slightly modified. It needs to specify a backing file for the memory so that an external process can access it.
Assuming you have `clear-kvm.img` and `custom-vmlinux.bin` on your system, here is the __cloud-hypervisor__ command you need to run:
Assuming you have `focal-server-cloudimg-amd64.raw` and `custom-vmlinux.bin` on your system, here is the __cloud-hypervisor__ command you need to run:
```bash
./cloud-hypervisor \
--cpus 4 \
--memory "size=512,file=/dev/shm" \
--disk path=clear-kvm.img \
--disk path=focal-server-cloudimg-amd64.raw \
--kernel custom-vmlinux.bin \
--cmdline "console=ttyS0 reboot=k panic=1 nomodules root=/dev/vda3" \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--fs tag=myfs,socket=/tmp/virtiofs,num_queues=1,queue_size=512
```

35
docs/fuzzing.md Normal file
View File

@@ -0,0 +1,35 @@
# Fuzzing in Cloud Hypervisor
Cloud Hypervisor uses [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) for fuzzing individual components.
The fuzzers are are in the `fuzz/fuzz_targets` directory
## Preparation
Switch to nightly:
````
rustup override set nightly
````
Install `cargo fuzz`:
```
cargo install cargo-fuzz
```
## Running the fuzzers
e.g. To run the `qcow` fuzzer using all available CPUs:
```
cargo fuzz run qcow -j `nproc`
```
## Adding a new fuzzer
```
cargo fuzz add <new_fuzzer>
```
Inspiration for fuzzers can be found in [crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/+/refs/heads/master/fuzz/)

View File

@@ -5,9 +5,7 @@ Currently Cloud Hypervisor only support hot plugging of CPU devices.
## Kernel support
For hotplug on Cloud Hypervisor ACPI GED support is needed. This can either be achieved by turning on `CONFIG_ACPI_REDUCED_HARDWARE_ONLY`
or by using this kernel patch (available in 5.5rc1 and later): https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/patch/drivers/acpi/Makefile?id=ac36d37e943635fc072e9d4f47e40a48fbcdb3f0
This patch is integrated into the Clear Linux KVM and cloudguest images.
or by using this kernel patch (available in 5.5-rc1 and later): https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/patch/drivers/acpi/Makefile?id=ac36d37e943635fc072e9d4f47e40a48fbcdb3f0
## CPU Hot Plug
@@ -22,8 +20,9 @@ To use CPU hotplug start the VM with the number of max vCPUs greater than the nu
$ pushd $CLOUDH
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
--kernel ./hypervisor-fw \
--disk path=clear-31890-kvm.img \
--kernel custom-vmlinux.bin \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--disk path=focal-server-cloudimg-amd64.raw \
--cpus boot=4,max=8 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
@@ -75,8 +74,9 @@ To use memory hotplug start the VM specifying some size RAM in the "hotplug_size
$ pushd $CLOUDH
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
--kernel ./hypervisor-fw \
--disk path=clear-31890-kvm.img \
--kernel custom-vmlinux.bin \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--disk path=focal-server-cloudimg-amd64.raw \
--cpus boot=4,max=8 \
--memory size=1024M,hotplug_size=8192M \
--net "tap=,mac=,ip=,mask=" \
@@ -110,4 +110,4 @@ 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.
Memory and CPU resizing can be combined together into the same HTTP API request.

55
docs/intel_sgx.md Normal file
View File

@@ -0,0 +1,55 @@
# Intel SGX
Intel® Software Guard Extensions (Intel® SGX) is an Intel technology designed
to increase the security of application code and data. Cloud-Hypervisor supports
SGX virtualization through KVM. Because SGX is built on hardware features that
cannot be emulated in software, virtualizing SGX requires support in KVM and in
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
buit using the [SGX Linux Development Tree](https://github.com/jsakkine-intel/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.
For more information about SGX, please refer to the [SGX Homepage](https://software.intel.com/sgx).
For more information about SGX SDK and how to test SGX, please refer to the
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
the guest.
In order to use SGX enclaves within a Cloud-Hypervisor VM, we must define one
or several Enclave Page Cache (EPC) sections. Here is an example of a VM being
created with 2 EPC sections, the first one being 64MiB with pre-allocated
memory, the second one being 32MiB with no pre-allocated memory.
```bash
./cloud-hypervisor \
--cpus boot=1 \
--memory size=1G \
--disk path=focal-server-cloudimg-amd64.raw \
--kernel bzImage \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--sgx-epc size=64M,prefault=on size=32M,prefault=off
```
Once booted, and assuming your guest kernel contains the patches from the
[KVM SGX Tree](https://github.com/intel/kvm-sgx), you can validate SGX devices
have been correctly created under `/dev/sgx`:
```bash
ls /dev/sgx
enclave provision 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.
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
the e820 table. It is treated yet as another device, which means it should
appear at the end of the guest address space.

View File

@@ -88,9 +88,9 @@ virtual IOMMU:
./cloud-hypervisor \
--cpus 1 \
--memory size=512M \
--disk path=clear-kvm.img,iommu=on \
--disk path=focal-server-cloudimg-amd64.raw,iommu=on \
--kernel custom-bzImage \
--cmdline "console=ttyS0 root=/dev/vda3" \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
```
From a guest perspective, it is easy to verify if the device is protected by
@@ -165,9 +165,9 @@ be consumed.
./cloud-hypervisor \
--cpus 1 \
--memory size=8G,file=/dev/hugepages \
--disk path=clear-kvm.img \
--disk path=focal-server-cloudimg-amd64.raw \
--kernel custom-bzImage \
--cmdline "console=ttyS0 root=/dev/vda3 hugepagesz=2M hugepages=2048" \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw hugepagesz=2M hugepages=2048" \
--net tap=,mac=,iommu=on
```
@@ -182,9 +182,9 @@ passing through is `0000:00:01.0`.
./cloud-hypervisor \
--cpus 1 \
--memory size=8G,file=/dev/hugepages \
--disk path=clear-kvm.img \
--disk path=focal-server-cloudimg-amd64.raw \
--kernel custom-bzImage \
--cmdline "console=ttyS0 root=/dev/vda3 kvm-intel.nested=1 vfio_iommu_type1.allow_unsafe_interrupts rw hugepagesz=2M hugepages=2048" \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw kvm-intel.nested=1 vfio_iommu_type1.allow_unsafe_interrupts rw hugepagesz=2M hugepages=2048" \
--device path=/sys/bus/pci/devices/0000:00:01.0,iommu=on
```
@@ -202,8 +202,8 @@ Last thing is to start the L2 guest with the huge pages memory backend.
./cloud-hypervisor \
--cpus 1 \
--memory size=4G,file=/dev/hugepages \
--disk path=clear-kvm.img \
--disk path=focal-server-cloudimg-amd64.raw \
--kernel custom-bzImage \
--cmdline "console=ttyS0 root=/dev/vda3" \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--device path=/sys/bus/pci/devices/0000:00:04.0
```

View File

@@ -25,9 +25,9 @@ Use one `--net` command-line argument from cloud-hypervisor to specify the emula
./cloud-hypervisor \
--cpus 4 \
--memory "size=512M" \
--disk path=my-root-disk.img \
--disk path=focal-server-cloudimg-amd64.raw \
--kernel my-vmlinux.bin \
--cmdline "console=ttyS0 reboot=k panic=1 nomodules root=/dev/vda3" \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--net tap=ich0,mac=a4:a1:c2:00:00:01,ip=192.168.4.2,mask=255.255.255.0,num_queues=2,queue_size=256 \
tap=ich1,mac=a4:a1:c2:00:00:02,ip=10.0.1.2,mask=255.255.255.0,num_queues=2,queue_size=256
```

112
docs/snapshot_restore.md Normal file
View File

@@ -0,0 +1,112 @@
# Snapshot and Restore
The goal for the snapshot/restore feature is to provide the user with the
ability to take a snapshot of a previously paused virtual machine. This
snapshot can be used as the base for creating new identical virtual machines,
without the need to boot them from scratch. The restore codepath takes the
snapshot and creates the exact same virtual machine, restoring the previously
saved states. The new virtual machine is restored in a paused state, as it was
before the snapshot was performed.
This feature is important for the project as it establishes the first step
towards the support for live migration.
## Snapshot a Cloud-Hypervisor VM
First thing, we must run a Cloud-Hypervisor VM:
```bash
./cloud-hypervisor \
--api-socket /tmp/cloud-hypervisor.sock \
--cpus boot=4 \
--memory size=4G \
--kernel bzImage \
--cmdline "root=/dev/vda1 console=hvc0 rw" \
--disk path=focal-server-cloudimg-amd64.raw
```
At any point in time when the VM is running, one might choose to pause it:
```bash
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock pause
```
Once paused, the VM can be safely snapshot into the specified directory and
using the following command:
```bash
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock snapshot file:///home/foo/snapshot
```
Given the directory was present on the system, the snapshot will succeed and
it should contain the following files:
```bash
ll /home/foo/snapshot/
total 4194536
drwxrwxr-x 2 foo bar 4096 Jul 22 11:50 ./
drwxr-xr-x 47 foo bar 4096 Jul 22 11:47 ../
-rw------- 1 foo bar 3221225472 Jul 22 11:19 memory-region-0
-rw------- 1 foo bar 1073741824 Jul 22 11:19 memory-region-1
-rw------- 1 foo bar 217853 Jul 22 11:19 vm.json
```
In this particular example, we can observe that 2 memory region files were
created. That is explained by the size of the guest RAM, which is 4GiB in this
case. Because it exceeds 3GiB (which is where we can find a ~1GiB memory hole),
Cloud-Hypervisor needs 2 distincts memory regions to be created. Each memory
region's content is stored through a dedicated file, which explains why we end
up with 2 different files, the first one containing the guest RAM range 0-3GiB
and the second one containing the guest RAM range 3-4GiB.
`vm.json` gathers all information related to the virtual machine configuration
and state. The configuration bits are used to create a similar virtual machine
with the correct amount of CPUs, RAM, and other expected devices. The state
bits are used to restore each component in the state it was left before the
snapshot occured.
## Restore a Cloud-Hypervisor VM
Given that one has access to an existing snapshot in `/home/foo/snapshot`,
it is possible to create a new VM based on this snapshot with the following
command:
```bash
./cloud-hypervisor \
--api-socket /tmp/cloud-hypervisor.sock \
--restore source_url=file:///home/foo/snapshot
```
Or using two different commands from two terminals:
```bash
# First terminal
./cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
# Second terminal
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock restore source_url=file:///home/foo/snapshot
```
Remember the VM is restored in a `paused` state, which was the VM's state when
it was snapshot. For this reason, one must explicitly `resume` the VM before to
start using it.
```bash
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock resume
```
At this point, the VM is fully restored and is identical to the VM which was
snapshot earlier.
## Limitations
The support of snapshot/restore feature is still experimental, meaning one
might still find some bugs associated with it.
Additionally, some devices and features don't support to be snapshot and
restored yet:
- `vhost-user` devices
- `virtio-mem`
- Intel SGX
VFIO devices are out of scope.

View File

@@ -66,10 +66,10 @@ takes the device's sysfs path as an argument. In our example it is
```
./target/debug/cloud-hypervisor \
--kernel ~/vmlinux \
--disk path=~/clear-29160-kvm.img \
--disk path=~/focal-server-cloudimg-amd64.raw \
--console off \
--serial tty \
--cmdline "console=ttyS0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda3" \
--cmdline "console=ttyS0 root=/dev/vda1 rw" \
--cpus 4 \
--memory size=512M \
--device path=/sys/bus/pci/devices/0000:01:00.0/

View File

@@ -73,10 +73,10 @@ VMs run in client mode. They connect to the socket created by the `dpdkvhostuser
# From the test terminal. We need to create one vhost-user-blk device for the --disk.
./cloud-hypervisor \
--cpus boot=4 \
--memory size=1024M,file=/dev/hugepages \
--memory size=1024M,hugepages=on \
--kernel linux/arch/x86/boot/compressed/vmlinux.bin \
--cmdline "console=ttyS0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda3 iommu=off" \
--disk "path=images/clear-kvm.img" "num_queues=4,queue_size=128,vhost_user=true,socket=/var/tmp/vhost.1" \
--cmdline "console=ttyS0 root=/dev/vda1 rw iommu=off" \
--disk path=images/focal-server-cloudimg-amd64.raw vhost_user=true,socket=/var/tmp/vhost.1,num_queues=4,queue_size=128 \
--console off \
--serial tty \
--rng
@@ -88,11 +88,11 @@ login in guest
# Use lsblk command to find out vhost-user-blk device
lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
vda 253:0 0 8.5G 0 disk
├─vda1 253:1 0 511M 0 part
├─vda2 253:2 0 32M 0 part [SWAP]
└─vda3 253:3 0 8G 0 part /
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
vda 252:0 0 2.2G 0 disk
├─vda1 252:1 0 2.1G 0 part /
├─vda14 252:14 0 4M 0 part
└─vda15 252:15 0 106M 0 part /boot/efi
vdb 253:16 0 512M 0 disk
The vhost-user-blk device is /dev/vdb

View File

@@ -76,19 +76,19 @@ VMs run in client mode. They connect to the socket created by the `dpdkvhostuser
# From one terminal. We need to give the cloud-hypervisor binary the NET_ADMIN capabilities for it to set TAP interfaces up on the host.
./cloud-hypervisor \
--cpus boot=2 \
--memory size=512M,file=/dev/hugepages \
--memory size=512M,hugepages=on \
--kernel vmlinux \
--cmdline "reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda3" \
--disk path=clear-kvm.img \
--net "mac=52:54:00:02:d9:01,vhost_user=true,socket=/var/run/openvswitch/vhost-user1,num_queues=4"
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--disk path=focal-server-cloudimg-amd64.raw \
--net mac=52:54:00:02:d9:01,vhost_user=true,socket=/var/run/openvswitch/vhost-user1,num_queues=4
# From another terminal. We need to give the cloud-hypervisor binary the NET_ADMIN capabilities for it to set TAP interfaces up on the host.
./cloud-hypervisor \
--cpus boot=2 \
--memory size=512M,file=/dev/hugepages \
--memory size=512M,hugepages=on \
--kernel vmlinux \
--cmdline "reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda3" \
--disk path=clear-kvm.img \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--disk path=focal-server-cloudimg-amd64.raw \
--net "mac=52:54:20:11:C5:02,vhost_user=true,socket=/var/run/openvswitch/vhost-user2,num_queues=4"
```

4
fuzz/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
target
corpus
artifacts

41
fuzz/Cargo.toml Normal file
View File

@@ -0,0 +1,41 @@
[package]
name = "cloud-hypervisor-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"
[package.metadata]
cargo-fuzz = true
[dependencies]
libc = "0.2.72"
libfuzzer-sys = "0.3"
qcow = { path = "../qcow" }
virtio-devices = { path = "../virtio-devices" }
vmm-sys-util = ">=0.3.1"
vm-virtio = { path = "../vm-virtio" }
vm-memory = "0.2.1"
[dependencies.cloud-hypervisor]
path = ".."
[patch.crates-io]
vm-memory = { git = "https://github.com/cloud-hypervisor/vm-memory", branch = "ch" }
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bin]]
name = "qcow"
path = "fuzz_targets/qcow.rs"
test = false
doc = false
[[bin]]
name = "block"
path = "fuzz_targets/block.rs"
test = false
doc = false

136
fuzz/fuzz_targets/block.rs Normal file
View File

@@ -0,0 +1,136 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![no_main]
use libfuzzer_sys::fuzz_target;
use std::ffi;
use std::fs::File;
use std::io::{self, Cursor, Read, Seek, SeekFrom};
use std::mem::size_of;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::path::PathBuf;
use std::sync::Arc;
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
use vm_virtio::Queue;
use vmm_sys_util::eventfd::EventFd;
const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
const CMD_SIZE: usize = 16; // Bytes in the command.
fuzz_target!(|bytes| {
let size_u64 = size_of::<u64>();
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE as usize)]).unwrap();
// The fuzz data is interpreted as:
// starting index 8 bytes
// command location 8 bytes
// command 16 bytes
// descriptors circular buffer 16 bytes * 3
if bytes.len() < 4 * size_u64 {
// Need an index to start.
return;
}
let mut data_image = Cursor::new(bytes);
let first_index = read_u64(&mut data_image);
if first_index > MEM_SIZE / DESC_SIZE {
return;
}
let first_offset = first_index * DESC_SIZE;
if first_offset as usize + size_u64 > bytes.len() {
return;
}
let command_addr = read_u64(&mut data_image);
if command_addr > MEM_SIZE - CMD_SIZE as u64 {
return;
}
if mem
.write_slice(
&bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
GuestAddress(command_addr as u64),
)
.is_err()
{
return;
}
data_image.seek(SeekFrom::Start(first_offset)).unwrap();
let desc_table = read_u64(&mut data_image);
if mem
.write_slice(&bytes[32..], GuestAddress(desc_table as u64))
.is_err()
{
return;
}
let mut q = Queue::new(QUEUE_SIZE);
q.ready = true;
q.size = QUEUE_SIZE / 2;
q.max_size = QUEUE_SIZE;
let queue_evts: Vec<EventFd> = vec![EventFd::new(0).unwrap()];
let queue_fd = queue_evts[0].as_raw_fd();
let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(queue_fd)) };
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 mut block = Block::new(
"tmp".to_owned(),
raw_img,
PathBuf::from(""),
false,
false,
2,
256,
)
.unwrap();
block
.activate(
GuestMemoryAtomic::new(mem),
Arc::new(NoopVirtioInterrupt {}),
vec![q],
queue_evts,
)
.ok();
queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
});
fn read_u64<T: Read>(readable: &mut T) -> u64 {
let mut buf = [0u8; size_of::<u64>()];
readable.read_exact(&mut buf[..]).unwrap();
u64::from_le_bytes(buf)
}
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
if res < 0 {
Err(io::Error::last_os_error())
} else {
Ok(res as RawFd)
}
}
pub struct NoopVirtioInterrupt {}
impl VirtioInterrupt for NoopVirtioInterrupt {
fn trigger(
&self,
_int_type: &VirtioInterruptType,
_queue: Option<&Queue>,
) -> std::result::Result<(), std::io::Error> {
Ok(())
}
}

49
fuzz/fuzz_targets/qcow.rs Normal file
View File

@@ -0,0 +1,49 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
#![no_main]
use libfuzzer_sys::fuzz_target;
use qcow::{QcowFile, RawFile};
use std::ffi;
use std::fs::File;
use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};
use std::mem::size_of;
use std::os::unix::io::{FromRawFd, RawFd};
// Take the first 64 bits of data as an address and the next 64 bits as data to
// store there. The rest of the data is used as a qcow image.
fuzz_target!(|bytes| {
if bytes.len() < 16 {
// Need an address and data, each are 8 bytes.
return;
}
let mut disk_image = Cursor::new(bytes);
let addr = read_u64(&mut disk_image);
let value = read_u64(&mut disk_image);
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
let mut disk_file: File = unsafe { File::from_raw_fd(shm) };
disk_file.write_all(&bytes[16..]).unwrap();
disk_file.seek(SeekFrom::Start(0)).unwrap();
if let Ok(mut qcow) = QcowFile::from(RawFile::new(disk_file, false)) {
if qcow.seek(SeekFrom::Start(addr)).is_ok() {
let _ = qcow.write_all(&value.to_le_bytes());
}
}
});
fn read_u64<T: Read>(readable: &mut T) -> u64 {
let mut buf = [0u8; size_of::<u64>()];
readable.read_exact(&mut buf[..]).unwrap();
u64::from_le_bytes(buf)
}
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {
let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) };
if res < 0 {
Err(io::Error::last_os_error())
} else {
Ok(res as RawFd)
}
}

25
hypervisor/Cargo.toml Normal file
View File

@@ -0,0 +1,25 @@
[package]
name = "hypervisor"
version = "0.1.0"
authors = ["Muminul Islam <muislam@microsoft.com>"]
edition = "2018"
[features]
kvm = []
[dependencies]
anyhow = "1.0"
thiserror = "1.0"
libc = "0.2.74"
log = "0.4.11"
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch", features = ["with-serde", "fam-wrappers"] }
serde = {version = ">=1.0.27", features = ["rc"] }
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"
vm-memory = { version = "0.2.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"]

View File

@@ -0,0 +1,15 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
#[cfg(target_arch = "x86_64")]
pub mod x86;

View File

@@ -0,0 +1,36 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
#[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
pub const MTRR_ENABLE: u64 = 0x800; // IA32_MTRR_DEF_TYPE MSR: E (MTRRs enabled) flag, bit 11
pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
// IOAPIC pins
pub const NUM_IOAPIC_PINS: usize = 24;

312
hypervisor/src/cpu.rs Normal file
View File

@@ -0,0 +1,312 @@
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
// Copyright 2018-2019 CrowdStrike, Inc.
//
//
#[cfg(target_arch = "aarch64")]
use crate::aarch64::VcpuInit;
use crate::{CpuState, MpState};
#[cfg(target_arch = "x86_64")]
use crate::x86_64::{
CpuId, ExtendedControlRegisters, FpuState, LapicState, MsrEntries, SpecialRegisters,
StandardRegisters, VcpuEvents, Xsave,
};
use thiserror::Error;
#[derive(Error, Debug)]
///
/// Enum for CPU error
pub enum HypervisorCpuError {
///
/// Setting standard registers error
///
#[error("Failed to set standard register: {0}")]
SetStandardRegs(#[source] anyhow::Error),
///
/// Setting standard registers error
///
#[error("Failed to get standard registers: {0}")]
GetStandardRegs(#[source] anyhow::Error),
///
/// Setting special register error
///
#[error("Failed to set special registers: {0}")]
SetSpecialRegs(#[source] anyhow::Error),
///
/// Getting standard register error
///
#[error("Failed to get special registers: {0}")]
GetSpecialRegs(#[source] anyhow::Error),
///
/// Setting floating point registers error
///
#[error("Failed to set special register: {0}")]
SetFloatingPointRegs(#[source] anyhow::Error),
///
/// Getting floating point register error
///
#[error("Failed to get special register: {0}")]
GetFloatingPointRegs(#[source] anyhow::Error),
///
/// Setting Cpuid error
///
#[error("Failed to set Cpuid: {0}")]
SetCpuid(#[source] anyhow::Error),
///
/// Getting Cpuid error
///
#[error("Failed to get Cpuid: {0}")]
GetCpuid(#[source] anyhow::Error),
///
/// Setting lapic state error
///
#[error("Failed to set Lapic state: {0}")]
SetLapicState(#[source] anyhow::Error),
///
/// Getting Lapic state error
///
#[error("Failed to get Lapic state: {0}")]
GetlapicState(#[source] anyhow::Error),
///
/// Setting MSR entries error
///
#[error("Failed to set Msr entries: {0}")]
SetMsrEntries(#[source] anyhow::Error),
///
/// Getting Msr entries error
///
#[error("Failed to get Msr entries: {0}")]
GetMsrEntries(#[source] anyhow::Error),
///
/// Setting MSR entries error
///
#[error("Failed to set MP state: {0}")]
SetMpState(#[source] anyhow::Error),
///
/// Getting Msr entries error
///
#[error("Failed to get MP state: {0}")]
GetMpState(#[source] anyhow::Error),
///
/// Setting Saved Processor Extended States error
///
#[error("Failed to set Saved Processor Extended States: {0}")]
SetXsaveState(#[source] anyhow::Error),
///
/// Getting Saved Processor Extended States error
///
#[error("Failed to get Saved Processor Extended States: {0}")]
GetXsaveState(#[source] anyhow::Error),
///
/// Setting Extended Control Registers error
///
#[error("Failed to set Extended Control Registers: {0}")]
SetXcsr(#[source] anyhow::Error),
///
/// Getting Extended Control Registers error
///
#[error("Failed to get Extended Control Registers: {0}")]
GetXcsr(#[source] anyhow::Error),
///
/// Running Vcpu error
///
#[error("Failed to run vcpu: {0}")]
RunVcpu(#[source] anyhow::Error),
///
/// Getting Vcpu events error
///
#[error("Failed to get Vcpu events: {0}")]
GetVcpuEvents(#[source] anyhow::Error),
///
/// Setting Vcpu events error
///
#[error("Failed to set Vcpu events: {0}")]
SetVcpuEvents(#[source] anyhow::Error),
///
/// Vcpu Init error
///
#[error("Failed to init vcpu: {0}")]
VcpuInit(#[source] anyhow::Error),
///
/// Setting one reg error
///
#[error("Failed to init vcpu: {0}")]
SetOneReg(#[source] anyhow::Error),
///
/// Getting one reg error
///
#[error("Failed to init vcpu: {0}")]
GetOneReg(#[source] anyhow::Error),
///
/// Getting guest clock paused error
///
#[error("Failed to notify guest its clock was paused: {0}")]
NotifyGuestClockPaused(#[source] anyhow::Error),
}
#[derive(Debug)]
pub enum VmExit<'a> {
#[cfg(target_arch = "x86_64")]
IoOut(u16 /* port */, &'a [u8] /* data */),
#[cfg(target_arch = "x86_64")]
IoIn(u16 /* port */, &'a mut [u8] /* data */),
#[cfg(target_arch = "x86_64")]
IoapicEoi(u8 /* vector */),
MmioRead(u64 /* address */, &'a mut [u8]),
MmioWrite(u64 /* address */, &'a [u8]),
Ignore,
Reset,
}
///
/// Result type for returning from a function
///
pub type Result<T> = anyhow::Result<T, HypervisorCpuError>;
///
/// Trait to represent a generic Vcpu
///
pub trait Vcpu: Send + Sync {
#[cfg(target_arch = "x86_64")]
///
/// Returns the vCPU general purpose registers.
///
fn get_regs(&self) -> Result<StandardRegisters>;
#[cfg(target_arch = "x86_64")]
///
/// Sets the vCPU general purpose registers.
///
fn set_regs(&self, regs: &StandardRegisters) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// Returns the vCPU special registers.
///
fn get_sregs(&self) -> Result<SpecialRegisters>;
#[cfg(target_arch = "x86_64")]
///
/// Sets the vCPU special registers
///
fn set_sregs(&self, sregs: &SpecialRegisters) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// Returns the floating point state (FPU) from the vCPU.
///
fn get_fpu(&self) -> Result<FpuState>;
#[cfg(target_arch = "x86_64")]
///
/// Set the floating point state (FPU) of a vCPU
///
fn set_fpu(&self, fpu: &FpuState) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call to setup the CPUID registers.
///
fn set_cpuid2(&self, cpuid: &CpuId) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call to retrieve the CPUID registers.
///
fn get_cpuid2(&self, num_entries: usize) -> Result<CpuId>;
#[cfg(target_arch = "x86_64")]
///
/// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
///
fn get_lapic(&self) -> Result<LapicState>;
#[cfg(target_arch = "x86_64")]
///
/// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller).
///
fn set_lapic(&self, lapic: &LapicState) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// Returns the model-specific registers (MSR) for this vCPU.
///
fn get_msrs(&self, msrs: &mut MsrEntries) -> Result<usize>;
#[cfg(target_arch = "x86_64")]
///
/// Setup the model-specific registers (MSR) for this vCPU.
///
fn set_msrs(&self, msrs: &MsrEntries) -> Result<usize>;
///
/// Returns the vcpu's current "multiprocessing state".
///
fn get_mp_state(&self) -> Result<MpState>;
///
/// Sets the vcpu's current "multiprocessing state".
///
fn set_mp_state(&self, mp_state: MpState) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that returns the vcpu's current "xsave struct".
///
fn get_xsave(&self) -> Result<Xsave>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that sets the vcpu's current "xsave struct".
///
fn set_xsave(&self, xsave: &Xsave) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that returns the vcpu's current "xcrs".
///
fn get_xcrs(&self) -> Result<ExtendedControlRegisters>;
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call that sets the vcpu's current "xcrs".
///
fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> Result<()>;
#[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) -> Result<VcpuEvents>;
#[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) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// Let the guest know that it has been paused, which prevents from
/// potential soft lockups when being resumed.
///
fn notify_guest_clock_paused(&self) -> Result<()>;
///
/// Sets the type of CPU to be exposed to the guest and optional features.
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn vcpu_init(&self, kvi: &VcpuInit) -> Result<()>;
///
/// Sets the value of one register for this vCPU.
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()>;
///
/// Sets the value of one register for this vCPU.
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn get_one_reg(&self, reg_id: u64) -> Result<u64>;
///
/// Retrieve the vCPU state.
/// This function is necessary to snapshot the VM
///
fn state(&self) -> Result<CpuState>;
///
/// Set the vCPU state.
/// This function is required when restoring the VM
///
fn set_state(&self, state: &CpuState) -> Result<()>;
///
/// Triggers the running of the current virtual CPU returning an exit reason.
///
fn run(&self) -> std::result::Result<VmExit, HypervisorCpuError>;
}

40
hypervisor/src/device.rs Normal file
View File

@@ -0,0 +1,40 @@
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
// Copyright 2018-2019 CrowdStrike, Inc.
//
// Copyright 2020, ARM Limited
//
use crate::DeviceAttr;
use std::os::unix::io::AsRawFd;
use thiserror::Error;
#[derive(Error, Debug)]
///
/// Enum for device error
pub enum HypervisorDeviceError {
///
/// Set device attribute error
///
#[error("Failed to set device attribute: {0}")]
SetDeviceAttribute(#[source] anyhow::Error),
}
///
/// Result type for returning from a function
///
pub type Result<T> = std::result::Result<T, HypervisorDeviceError>;
///
/// Trait to represent a device
///
/// This crate provides a hypervisor-agnostic interfaces for device
///
pub trait Device: Send + Sync + AsRawFd {
/// Set device attribute.
fn set_device_attr(&self, attr: &DeviceAttr) -> Result<()>;
}

View File

@@ -0,0 +1,116 @@
// 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::vm::Vm;
#[cfg(target_arch = "x86_64")]
use crate::x86_64::{CpuId, MsrList};
#[cfg(target_arch = "x86_64")]
use kvm_ioctls::Cap;
use std::sync::Arc;
use thiserror::Error;
#[derive(Error, Debug)]
///
///
pub enum HypervisorError {
///
/// Vm creation failure
///
#[error("Failed to create Vm: {0}")]
VmCreate(#[source] anyhow::Error),
///
/// Vm setup failure
///
#[error("Failed to setup Vm: {0}")]
VmSetup(#[source] anyhow::Error),
///
/// API version error
///
#[error("Failed to get API Version: {0}")]
GetApiVersion(#[source] anyhow::Error),
///
/// Vcpu mmap error
///
#[error("Failed to get Vcpu Mmap: {0}")]
GetVcpuMmap(#[source] anyhow::Error),
///
/// Max Vcpu error
///
#[error("Failed to get number of max vcpus: {0}")]
GetMaxVcpu(#[source] anyhow::Error),
///
/// Recommended Vcpu error
///
#[error("Failed to get number of max vcpus: {0}")]
GetNrVcpus(#[source] anyhow::Error),
///
/// CpuId error
///
#[error("Failed to get number of max vcpus: {0}")]
GetCpuId(#[source] anyhow::Error),
///
/// Failed to retrieve list of MSRs.
///
#[error("Failed to get the list of supported MSRs: {0}")]
GetMsrList(#[source] anyhow::Error),
}
///
/// Result type for returning from a function
///
pub type Result<T> = std::result::Result<T, HypervisorError>;
///
/// Trait to represent a Hypervisor
///
/// This crate provides a hypervisor-agnostic interfaces
///
pub trait Hypervisor: Send + Sync {
///
/// Create a Vm using the underlying hypervisor
/// Return a hypervisor-agnostic Vm trait object
///
fn create_vm(&self) -> Result<Arc<dyn Vm>>;
///
/// Get the API version of the hypervisor
///
fn get_api_version(&self) -> i32;
///
/// Returns the size of the memory mapping required to use the vcpu's structures
///
fn get_vcpu_mmap_size(&self) -> Result<usize>;
///
/// Gets the recommended maximum number of VCPUs per VM.
///
fn get_max_vcpus(&self) -> Result<usize>;
///
/// Gets the recommended number of VCPUs per VM.
///
fn get_nr_vcpus(&self) -> Result<usize>;
#[cfg(target_arch = "x86_64")]
///
/// Checks if a particular `Cap` is available.
///
fn check_capability(&self, c: Cap) -> bool;
#[cfg(target_arch = "x86_64")]
///
/// Get the supported CpuID
///
fn get_cpuid(&self) -> Result<CpuId>;
///
/// Check particular extensions if any
///
fn check_required_extensions(&self) -> Result<()>;
#[cfg(target_arch = "x86_64")]
///
/// Retrieve the list of MSRs supported by the hypervisor.
///
fn get_msr_list(&self) -> Result<MsrList>;
}

View File

@@ -0,0 +1,27 @@
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
// Copyright 2018-2019 CrowdStrike, Inc.
//
//
///
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
///
use crate::kvm::{KvmError, KvmResult};
pub use kvm_bindings::kvm_vcpu_init as VcpuInit;
use serde_derive::{Deserialize, Serialize};
pub use {kvm_ioctls::Cap, kvm_ioctls::Kvm};
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
if !kvm.check_extension(Cap::SignalMsi) {
return Err(KvmError::CapabilityMissing(Cap::SignalMsi));
}
Ok(())
}
#[derive(Clone, Serialize, Deserialize)]
pub struct VcpuKvmState {}

953
hypervisor/src/kvm/mod.rs Normal file
View File

@@ -0,0 +1,953 @@
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
// Copyright 2018-2019 CrowdStrike, Inc.
//
//
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
use std::os::unix::io::{AsRawFd, RawFd};
use std::result;
use std::sync::Arc;
#[cfg(target_arch = "x86_64")]
use vm_memory::Address;
use vmm_sys_util::eventfd::EventFd;
#[cfg(target_arch = "aarch64")]
pub use crate::aarch64::{check_required_kvm_extensions, VcpuInit, VcpuKvmState as CpuState};
use crate::cpu;
use crate::device;
use crate::hypervisor;
use crate::vm;
// x86_64 dependencies
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
#[cfg(target_arch = "x86_64")]
use x86_64::{
check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters, KVM_TSS_ADDRESS,
};
#[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, MsrList, 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;
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};
///
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
///
pub use {
kvm_bindings::kvm_clock_data as ClockData, kvm_bindings::kvm_create_device as CreateDevice,
kvm_bindings::kvm_device_attr as DeviceAttr,
kvm_bindings::kvm_irq_routing_entry as IrqRoutingEntry, kvm_bindings::kvm_mp_state as MpState,
kvm_bindings::kvm_userspace_memory_region as MemoryRegion,
kvm_bindings::kvm_vcpu_events as VcpuEvents, kvm_ioctls::DeviceFd, kvm_ioctls::IoEventAddress,
kvm_ioctls::VcpuExit,
};
/// Wrapper over KVM VM ioctls.
pub struct KvmVm {
fd: Arc<VmFd>,
#[cfg(target_arch = "x86_64")]
msrs: MsrEntries,
}
// 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)
}
///
/// Implementation of Vm trait for KVM
/// Example:
/// #[cfg(feature = "kvm")]
/// extern crate hypervisor
/// 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");
/// vm.set/get().unwrap()
///
impl vm::Vm for KvmVm {
#[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<()> {
self.fd
.set_tss_address(offset)
.map_err(|e| vm::HypervisorVmError::SetTssAddress(e.into()))
}
///
/// Creates an in-kernel interrupt controller.
///
fn create_irq_chip(&self) -> vm::Result<()> {
self.fd
.create_irq_chip()
.map_err(|e| vm::HypervisorVmError::CreateIrq(e.into()))
}
///
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
///
fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
self.fd
.register_irqfd(fd, gsi)
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))
}
///
/// Unregisters an event that will, when signaled, trigger the `gsi` IRQ.
///
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
self.fd
.unregister_irqfd(fd, gsi)
.map_err(|e| vm::HypervisorVmError::UnregisterIrqFd(e.into()))
}
///
/// Creates a VcpuFd object from a vcpu RawFd.
///
fn create_vcpu(&self, id: u8) -> vm::Result<Arc<dyn cpu::Vcpu>> {
let vc = self
.fd
.create_vcpu(id)
.map_err(|e| vm::HypervisorVmError::CreateVcpu(e.into()))?;
let vcpu = KvmVcpu {
fd: vc,
#[cfg(target_arch = "x86_64")]
msrs: self.msrs.clone(),
};
Ok(Arc::new(vcpu))
}
///
/// Registers an event to be signaled whenever a certain address is written to.
///
fn register_ioevent(
&self,
fd: &EventFd,
addr: &IoEventAddress,
datamatch: Option<vm::DataMatch>,
) -> vm::Result<()> {
if let Some(dm) = datamatch {
match dm {
vm::DataMatch::DataMatch32(kvm_dm32) => self
.fd
.register_ioevent(fd, addr, kvm_dm32)
.map_err(|e| vm::HypervisorVmError::RegisterIoEvent(e.into())),
vm::DataMatch::DataMatch64(kvm_dm64) => self
.fd
.register_ioevent(fd, addr, kvm_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()))
}
}
///
/// Unregisters an event from a certain address it has been previously registered to.
///
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> vm::Result<()> {
self.fd
.unregister_ioevent(fd, addr, NoDatamatch)
.map_err(|e| vm::HypervisorVmError::UnregisterIoEvent(e.into()))
}
///
/// Sets the GSI routing table entries, overwriting any previously set
/// entries, as per the `KVM_SET_GSI_ROUTING` ioctl.
///
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
let mut irq_routing =
vec_with_array_field::<kvm_irq_routing, kvm_irq_routing_entry>(entries.len());
irq_routing[0].nr = entries.len() as u32;
irq_routing[0].flags = 0;
unsafe {
let entries_slice: &mut [kvm_irq_routing_entry] =
irq_routing[0].entries.as_mut_slice(entries.len());
entries_slice.copy_from_slice(&entries);
}
self.fd
.set_gsi_routing(&irq_routing[0])
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
}
///
/// Creates a memory region structure that can be used with set_user_memory_region
///
fn make_user_memory_region(
&self,
slot: u32,
guest_phys_addr: u64,
memory_size: u64,
userspace_addr: u64,
readonly: bool,
) -> MemoryRegion {
MemoryRegion {
slot,
guest_phys_addr,
memory_size,
userspace_addr,
flags: if readonly { KVM_MEM_READONLY } else { 0 },
}
}
///
/// Creates/modifies a guest physical memory slot.
///
fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
// Safe because guest regions are guaranteed not to overlap.
unsafe {
self.fd
.set_user_memory_region(user_memory_region)
.map_err(|e| vm::HypervisorVmError::SetUserMemory(e.into()))
}
}
///
/// Creates an emulated device in the kernel.
///
/// See the documentation for `KVM_CREATE_DEVICE`.
fn create_device(&self, device: &mut CreateDevice) -> vm::Result<Arc<dyn device::Device>> {
let fd = self
.fd
.create_device(device)
.map_err(|e| vm::HypervisorVmError::CreateDevice(e.into()))?;
let device = KvmDevice { fd };
Ok(Arc::new(device))
}
///
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> vm::Result<()> {
self.fd
.get_preferred_target(kvi)
.map_err(|e| vm::HypervisorVmError::GetPreferredTarget(e.into()))
}
#[cfg(target_arch = "x86_64")]
fn enable_split_irq(&self) -> vm::Result<()> {
// Set TSS
self.fd
.set_tss_address(KVM_TSS_ADDRESS.raw_value() as usize)
.map_err(|e| vm::HypervisorVmError::EnableSplitIrq(e.into()))?;
// 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;
cap.args[0] = NUM_IOAPIC_PINS as u64;
self.fd
.enable_cap(&cap)
.map_err(|e| vm::HypervisorVmError::EnableSplitIrq(e.into()))?;
Ok(())
}
/// Retrieve guest clock.
#[cfg(target_arch = "x86_64")]
fn get_clock(&self) -> vm::Result<ClockData> {
self.fd
.get_clock()
.map_err(|e| vm::HypervisorVmError::GetClock(e.into()))
}
/// Set guest clock.
#[cfg(target_arch = "x86_64")]
fn set_clock(&self, data: &ClockData) -> vm::Result<()> {
self.fd
.set_clock(data)
.map_err(|e| vm::HypervisorVmError::SetClock(e.into()))
}
/// Checks if a particular `Cap` is available.
fn check_extension(&self, c: Cap) -> bool {
self.fd.check_extension(c)
}
/// Create a device that is used for passthrough
fn create_passthrough_device(&self) -> vm::Result<Arc<dyn device::Device>> {
let mut vfio_dev = kvm_create_device {
type_: kvm_device_type_KVM_DEV_TYPE_VFIO,
fd: 0,
flags: 0,
};
self.create_device(&mut vfio_dev)
.map_err(|e| vm::HypervisorVmError::CreatePassthroughDevice(e.into()))
}
}
/// Wrapper over KVM system ioctls.
pub struct KvmHypervisor {
kvm: Kvm,
}
/// Enum for KVM related error
#[derive(Debug)]
pub enum KvmError {
CapabilityMissing(Cap),
}
pub type KvmResult<T> = result::Result<T, KvmError>;
impl KvmHypervisor {
/// Create a hypervisor based on Kvm
pub fn new() -> hypervisor::Result<KvmHypervisor> {
let kvm_obj = Kvm::new().map_err(|e| hypervisor::HypervisorError::VmCreate(e.into()))?;
Ok(KvmHypervisor { kvm: kvm_obj })
}
}
/// Implementation of Hypervisor trait for KVM
/// Example:
/// #[cfg(feature = "kvm")]
/// extern crate hypervisor
/// 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");
///
impl hypervisor::Hypervisor for KvmHypervisor {
/// 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>> {
let fd: VmFd;
loop {
match self.kvm.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 vm_fd = Arc::new(fd);
#[cfg(target_arch = "x86_64")]
{
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 indices = msr_list.as_slice();
let msr_entries = msrs.as_mut_slice();
for (pos, index) in indices.iter().enumerate() {
msr_entries[pos].index = *index;
}
Ok(Arc::new(KvmVm { fd: vm_fd, msrs }))
}
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
{
Ok(Arc::new(KvmVm { fd: vm_fd }))
}
}
fn check_required_extensions(&self) -> hypervisor::Result<()> {
check_required_kvm_extensions(&self.kvm).expect("Missing KVM capabilities");
Ok(())
}
///
/// Returns the KVM API version.
///
fn get_api_version(&self) -> i32 {
self.kvm.get_api_version()
}
///
/// Returns the size of the memory mapping required to use the vcpu's `kvm_run` structure.
///
fn get_vcpu_mmap_size(&self) -> hypervisor::Result<usize> {
self.kvm
.get_vcpu_mmap_size()
.map_err(|e| hypervisor::HypervisorError::GetVcpuMmap(e.into()))
}
///
/// Gets the recommended maximum number of VCPUs per VM.
///
fn get_max_vcpus(&self) -> hypervisor::Result<usize> {
Ok(self.kvm.get_max_vcpus())
}
///
/// Gets the recommended number of VCPUs per VM.
///
fn get_nr_vcpus(&self) -> hypervisor::Result<usize> {
Ok(self.kvm.get_nr_vcpus())
}
#[cfg(target_arch = "x86_64")]
///
/// Checks if a particular `Cap` is available.
///
fn check_capability(&self, c: Cap) -> bool {
self.kvm.check_extension(c)
}
#[cfg(target_arch = "x86_64")]
///
/// X86 specific call to get the system supported CPUID values.
///
fn get_cpuid(&self) -> hypervisor::Result<CpuId> {
self.kvm
.get_supported_cpuid(kvm_bindings::KVM_MAX_CPUID_ENTRIES)
.map_err(|e| hypervisor::HypervisorError::GetCpuId(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Retrieve the list of MSRs supported by KVM.
///
fn get_msr_list(&self) -> hypervisor::Result<MsrList> {
self.kvm
.get_msr_index_list()
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
}
}
/// Vcpu struct for KVM
pub struct KvmVcpu {
fd: VcpuFd,
#[cfg(target_arch = "x86_64")]
msrs: MsrEntries,
}
/// Implementation of Vcpu trait for KVM
/// Example:
/// #[cfg(feature = "kvm")]
/// extern crate hypervisor
/// 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();
/// vcpu.get/set().unwrap()
///
impl cpu::Vcpu for KvmVcpu {
#[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 using the `KVM_SET_REGS` ioctl.
///
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 using the `KVM_SET_SREGS` ioctl.
///
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 using the `KVM_SET_FPU` ioct.
///
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")]
///
/// X86 specific call to setup the CPUID registers.
///
fn set_cpuid2(&self, cpuid: &CpuId) -> cpu::Result<()> {
self.fd
.set_cpuid2(cpuid)
.map_err(|e| cpu::HypervisorCpuError::SetCpuid(e.into()))
}
///
/// X86 specific call to retrieve the CPUID registers.
///
#[cfg(target_arch = "x86_64")]
fn get_cpuid2(&self, num_entries: usize) -> cpu::Result<CpuId> {
self.fd
.get_cpuid2(num_entries)
.map_err(|e| cpu::HypervisorCpuError::GetCpuid(e.into()))
}
#[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, klapic: &LapicState) -> cpu::Result<()> {
self.fd
.set_lapic(klapic)
.map_err(|e| cpu::HypervisorCpuError::SetLapicState(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()))
}
///
/// Returns the vcpu's current "multiprocessing state".
///
fn get_mp_state(&self) -> cpu::Result<MpState> {
self.fd
.get_mp_state()
.map_err(|e| cpu::HypervisorCpuError::GetMpState(e.into()))
}
///
/// Sets the vcpu's current "multiprocessing state".
///
fn set_mp_state(&self, mp_state: MpState) -> cpu::Result<()> {
self.fd
.set_mp_state(mp_state)
.map_err(|e| cpu::HypervisorCpuError::SetMpState(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()))
}
#[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()))
}
///
/// Triggers the running of the current virtual CPU returning an exit reason.
///
fn run(&self) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
match self.fd.run() {
Ok(run) => match run {
#[cfg(target_arch = "x86_64")]
VcpuExit::IoIn(addr, data) => Ok(cpu::VmExit::IoIn(addr, data)),
#[cfg(target_arch = "x86_64")]
VcpuExit::IoOut(addr, data) => Ok(cpu::VmExit::IoOut(addr, data)),
#[cfg(target_arch = "x86_64")]
VcpuExit::IoapicEoi(vector) => Ok(cpu::VmExit::IoapicEoi(vector)),
#[cfg(target_arch = "x86_64")]
VcpuExit::Shutdown | VcpuExit::Hlt => Ok(cpu::VmExit::Reset),
#[cfg(target_arch = "aarch64")]
VcpuExit::SystemEvent(event_type, flags) => {
use kvm_bindings::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 {
Ok(cpu::VmExit::Reset)
} else {
Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
"Unexpected system event with type 0x{:x}, flags 0x{:x}",
event_type,
flags
)))
}
}
VcpuExit::MmioRead(addr, data) => Ok(cpu::VmExit::MmioRead(addr, data)),
VcpuExit::MmioWrite(addr, data) => Ok(cpu::VmExit::MmioWrite(addr, data)),
r => Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
"Unexpected exit reason on vcpu run: {:?}",
r
))),
},
Err(ref 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")]
///
/// 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")]
///
/// Let the guest know that it has been paused, which prevents from
/// potential soft lockups when being resumed.
///
fn notify_guest_clock_paused(&self) -> cpu::Result<()> {
self.fd
.kvmclock_ctrl()
.map_err(|e| cpu::HypervisorCpuError::NotifyGuestClockPaused(e.into()))
}
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn vcpu_init(&self, kvi: &VcpuInit) -> cpu::Result<()> {
self.fd
.vcpu_init(kvi)
.map_err(|e| cpu::HypervisorCpuError::VcpuInit(e.into()))
}
///
/// Sets the value of one register for this vCPU.
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn set_one_reg(&self, reg_id: u64, data: u64) -> cpu::Result<()> {
self.fd
.set_one_reg(reg_id, data)
.map_err(|e| cpu::HypervisorCpuError::SetOneReg(e.into()))
}
///
/// Gets the value of one register for this vCPU.
///
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn get_one_reg(&self, reg_id: u64) -> cpu::Result<u64> {
self.fd
.get_one_reg(reg_id)
.map_err(|e| cpu::HypervisorCpuError::GetOneReg(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Get the current CPU state
///
/// Ordering requirements:
///
/// KVM_GET_MP_STATE calls kvm_apic_accept_events(), which might modify
/// vCPU/LAPIC state. As such, it must be done before most everything
/// else, otherwise we cannot restore everything and expect it to work.
///
/// KVM_GET_VCPU_EVENTS/KVM_SET_VCPU_EVENTS is unsafe if other vCPUs are
/// still running.
///
/// KVM_GET_LAPIC may change state of LAPIC before returning it.
///
/// GET_VCPU_EVENTS should probably be last to save. The code looks as
/// it might as well be affected by internal state modifications of the
/// GET ioctls.
///
/// SREGS saves/restores a pending interrupt, similar to what
/// VCPU_EVENTS also does.
///
/// GET_MSRS requires a pre-populated data structure to do something
/// meaningful. For SET_MSRS it will then contain good data.
///
/// # Example
///
/// ```rust
/// # extern crate hypervisor;
/// # use hypervisor::KvmHypervisor;
/// # use std::sync::Arc;
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
/// 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 state = vcpu.state().unwrap();
/// ```
fn state(&self) -> cpu::Result<CpuState> {
let mp_state = self.get_mp_state()?;
let regs = self.get_regs()?;
let sregs = self.get_sregs()?;
let xsave = self.get_xsave()?;
let xcrs = self.get_xcrs()?;
let lapic_state = self.get_lapic()?;
let fpu = self.get_fpu()?;
// Try to get all MSRs based on the list previously retrieved from KVM.
// If the number of MSRs obtained from GET_MSRS is different from the
// expected amount, we fallback onto a slower method by getting MSRs
// by chunks. This is the only way to make sure we try to get as many
// MSRs as possible, even if some MSRs are not supported.
let mut msr_entries = self.msrs.clone();
let expected_num_msrs = msr_entries.as_fam_struct_ref().nmsrs as usize;
let num_msrs = self.get_msrs(&mut msr_entries)?;
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]);
loop {
warn!(
"Detected faulty MSR 0x{:x} while getting MSRs",
msr_entries.as_slice()[faulty_msr_index].index
);
let start_pos = faulty_msr_index + 1;
let mut sub_msr_entries =
MsrEntries::from_entries(&msr_entries.as_slice()[start_pos..]);
let expected_num_msrs = sub_msr_entries.as_fam_struct_ref().nmsrs as usize;
let num_msrs = self.get_msrs(&mut sub_msr_entries)?;
for i in 0..num_msrs {
msr_entries_tmp
.push(sub_msr_entries.as_slice()[i])
.map_err(|e| {
cpu::HypervisorCpuError::GetMsrEntries(anyhow!(
"Failed adding MSR entries: {:?}",
e
))
})?;
}
if num_msrs == expected_num_msrs {
break;
}
faulty_msr_index = start_pos + num_msrs;
}
msr_entries_tmp
} else {
msr_entries
};
let vcpu_events = self.get_vcpu_events()?;
Ok(CpuState {
msrs,
vcpu_events,
regs,
sregs,
fpu,
lapic_state,
xsave,
xcrs,
mp_state,
})
}
#[cfg(target_arch = "aarch64")]
fn state(&self) -> cpu::Result<CpuState> {
unimplemented!();
}
#[cfg(target_arch = "x86_64")]
///
/// Restore the previously saved CPU state
///
/// Ordering requirements:
///
/// KVM_GET_VCPU_EVENTS/KVM_SET_VCPU_EVENTS is unsafe if other vCPUs are
/// still running.
///
/// Some SET ioctls (like set_mp_state) depend on kvm_vcpu_is_bsp(), so
/// if we ever change the BSP, we have to do that before restoring anything.
/// The same seems to be true for CPUID stuff.
///
/// SREGS saves/restores a pending interrupt, similar to what
/// VCPU_EVENTS also does.
///
/// SET_REGS clears pending exceptions unconditionally, thus, it must be
/// done before SET_VCPU_EVENTS, which restores it.
///
/// SET_LAPIC must come after SET_SREGS, because the latter restores
/// the apic base msr.
///
/// SET_LAPIC must come before SET_MSRS, because the TSC deadline MSR
/// only restores successfully, when the LAPIC is correctly configured.
///
/// Arguments: CpuState
/// # Example
///
/// ```rust
/// # extern crate hypervisor;
/// # use hypervisor::KvmHypervisor;
/// # use std::sync::Arc;
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
/// 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 state = vcpu.state().unwrap();
/// vcpu.set_state(&state).unwrap();
/// ```
fn set_state(&self, state: &CpuState) -> cpu::Result<()> {
self.set_mp_state(state.mp_state)?;
self.set_regs(&state.regs)?;
self.set_sregs(&state.sregs)?;
self.set_xsave(&state.xsave)?;
self.set_xcrs(&state.xcrs)?;
self.set_lapic(&state.lapic_state)?;
self.set_fpu(&state.fpu)?;
// Try to set all MSRs previously stored.
// If the number of MSRs set from SET_MSRS is different from the
// expected amount, we fallback onto a slower method by setting MSRs
// by chunks. This is the only way to make sure we try to set as many
// MSRs as possible, even if some MSRs are not supported.
let expected_num_msrs = state.msrs.as_fam_struct_ref().nmsrs as usize;
let num_msrs = self.set_msrs(&state.msrs)?;
if num_msrs != expected_num_msrs {
let mut faulty_msr_index = num_msrs;
loop {
warn!(
"Detected faulty MSR 0x{:x} while setting MSRs",
state.msrs.as_slice()[faulty_msr_index].index
);
let start_pos = faulty_msr_index + 1;
let sub_msr_entries = MsrEntries::from_entries(&state.msrs.as_slice()[start_pos..]);
let expected_num_msrs = sub_msr_entries.as_fam_struct_ref().nmsrs as usize;
let num_msrs = self.set_msrs(&sub_msr_entries)?;
if num_msrs == expected_num_msrs {
break;
}
faulty_msr_index = start_pos + num_msrs;
}
}
self.set_vcpu_events(&state.vcpu_events)?;
Ok(())
}
#[allow(unused_variables)]
#[cfg(target_arch = "aarch64")]
fn set_state(&self, state: &CpuState) -> cpu::Result<()> {
Ok(())
}
}
/// Device struct for KVM
pub struct KvmDevice {
fd: DeviceFd,
}
impl device::Device for KvmDevice {
///
/// Set device attribute
///
fn set_device_attr(&self, attr: &DeviceAttr) -> device::Result<()> {
self.fd
.set_device_attr(attr)
.map_err(|e| device::HypervisorDeviceError::SetDeviceAttribute(e.into()))
}
}
impl AsRawFd for KvmDevice {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}

View File

@@ -0,0 +1,97 @@
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
// Copyright 2018-2019 CrowdStrike, Inc.
//
//
use vm_memory::GuestAddress;
use crate::arch::x86::{msr_index, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
use serde_derive::{Deserialize, Serialize};
///
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
///
pub use {
kvm_bindings::kvm_cpuid_entry2 as CpuIdEntry, kvm_bindings::kvm_dtable as DescriptorTable,
kvm_bindings::kvm_fpu as FpuState, kvm_bindings::kvm_lapic_state as LapicState,
kvm_bindings::kvm_mp_state as MpState, kvm_bindings::kvm_msr_entry as MsrEntry,
kvm_bindings::kvm_regs as StandardRegisters, kvm_bindings::kvm_segment as SegmentRegister,
kvm_bindings::kvm_sregs as SpecialRegisters, kvm_bindings::kvm_vcpu_events as VcpuEvents,
kvm_bindings::kvm_xcrs as ExtendedControlRegisters, kvm_bindings::kvm_xsave as Xsave,
kvm_bindings::CpuId, kvm_bindings::MsrList, kvm_bindings::Msrs as MsrEntries,
kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX as CPUID_FLAG_VALID_INDEX,
};
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
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()
}
};
}
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),
msr_data!(
msr_index::MSR_IA32_MISC_ENABLE,
msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64
),
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
])
}
///
/// Check KVM extension for Linux
///
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
if !kvm.check_extension(Cap::SignalMsi) {
return Err(KvmError::CapabilityMissing(Cap::SignalMsi));
}
if !kvm.check_extension(Cap::TscDeadlineTimer) {
return Err(KvmError::CapabilityMissing(Cap::TscDeadlineTimer));
}
if !kvm.check_extension(Cap::SplitIrqchip) {
return Err(KvmError::CapabilityMissing(Cap::SplitIrqchip));
}
Ok(())
}
#[derive(Clone, Serialize, Deserialize)]
pub struct VcpuKvmState {
pub msrs: MsrEntries,
pub vcpu_events: VcpuEvents,
pub regs: StandardRegisters,
pub sregs: SpecialRegisters,
pub fpu: FpuState,
pub lapic_state: LapicState,
pub xsave: Xsave,
pub xcrs: ExtendedControlRegisters,
pub mp_state: MpState,
}

60
hypervisor/src/lib.rs Normal file
View File

@@ -0,0 +1,60 @@
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
// Copyright 2018-2019 CrowdStrike, Inc.
//
//
//! A generic abstraction around hypervisor functionality
//!
//! This crate offers a trait abstraction for underlying hypervisors
//!
//! # Platform support
//!
//! - x86_64
//! - arm64
//!
#[macro_use]
extern crate anyhow;
#[macro_use]
extern crate log;
extern crate serde;
extern crate serde_derive;
extern crate serde_json;
extern crate thiserror;
/// KVM implementation module
pub mod kvm;
/// Hypevisor related module
pub mod hypervisor;
/// Vm related module
pub mod vm;
/// Architecture specific definitions
pub mod arch;
/// CPU related module
mod cpu;
/// Device related module
mod device;
pub use crate::hypervisor::{Hypervisor, HypervisorError};
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
pub use device::{Device, HypervisorDeviceError};
pub use kvm::*;
pub use vm::{DataMatch, HypervisorVmError, Vm};
use std::sync::Arc;
pub fn new() -> std::result::Result<Arc<dyn Hypervisor>, HypervisorError> {
#[cfg(feature = "kvm")]
let hv = kvm::KvmHypervisor::new()?;
Ok(Arc::new(hv))
}

182
hypervisor/src/vm.rs Normal file
View File

@@ -0,0 +1,182 @@
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
// Copyright © 2020, Microsoft Corporation
//
// Copyright 2018-2019 CrowdStrike, Inc.
//
//
#[cfg(target_arch = "aarch64")]
use crate::aarch64::VcpuInit;
use crate::cpu::Vcpu;
use crate::device::Device;
#[cfg(target_arch = "x86_64")]
use crate::ClockData;
use crate::{CreateDevice, IoEventAddress, IrqRoutingEntry, MemoryRegion};
use kvm_ioctls::Cap;
use std::sync::Arc;
use thiserror::Error;
use vmm_sys_util::eventfd::EventFd;
///
/// I/O events data matches (32 or 64 bits).
///
pub enum DataMatch {
DataMatch32(u32),
DataMatch64(u64),
}
impl Into<u64> for DataMatch {
fn into(self) -> u64 {
match self {
DataMatch::DataMatch32(dm) => dm.into(),
DataMatch::DataMatch64(dm) => dm,
}
}
}
#[derive(Error, Debug)]
///
/// Enum for VM error
pub enum HypervisorVmError {
///
/// Create Vcpu error
///
#[error("Failed to create Vcpu: {0}")]
CreateVcpu(#[source] anyhow::Error),
///
/// TSS address error
///
#[error("Failed to set TSS address: {0}")]
SetTssAddress(#[source] anyhow::Error),
///
/// Create interrupt controller error
///
#[error("Failed to create interrupt controller: {0}")]
CreateIrq(#[source] anyhow::Error),
///
/// Register interrupt event error
///
#[error("Failed to register interrupt event: {0}")]
RegisterIrqFd(#[source] anyhow::Error),
///
/// Un register interrupt event error
///
#[error("Failed to unregister interrupt event: {0}")]
UnregisterIrqFd(#[source] anyhow::Error),
///
/// Register IO event error
///
#[error("Failed to register IO event: {0}")]
RegisterIoEvent(#[source] anyhow::Error),
///
/// Unregister IO event error
///
#[error("Failed to unregister IO event: {0}")]
UnregisterIoEvent(#[source] anyhow::Error),
///
/// Set GSI routing error
///
#[error("Failed to set GSI routing: {0}")]
SetGsiRouting(#[source] anyhow::Error),
///
/// Set user memory error
///
#[error("Failed to set user memory: {0}")]
SetUserMemory(#[source] anyhow::Error),
///
/// Create device error
///
#[error("Failed to set GSI routing: {0}")]
CreateDevice(#[source] anyhow::Error),
///
/// Get preferred target error
///
#[error("Failed to get preferred target: {0}")]
GetPreferredTarget(#[source] anyhow::Error),
///
/// Enable split Irq error
///
#[error("Failed to enable split Irq: {0}")]
EnableSplitIrq(#[source] anyhow::Error),
///
/// Get clock error
///
#[error("Failed to get clock: {0}")]
GetClock(#[source] anyhow::Error),
///
/// Set clock error
///
#[error("Failed to set clock: {0}")]
SetClock(#[source] anyhow::Error),
///
/// Create passthrough device
///
#[error("Failed to create passthrough device: {0}")]
CreatePassthroughDevice(#[source] anyhow::Error),
}
///
/// Result type for returning from a function
///
pub type Result<T> = std::result::Result<T, HypervisorVmError>;
///
/// Trait to represent a Vm
///
/// This crate provides a hypervisor-agnostic interfaces for Vm
///
pub trait Vm: Send + Sync {
#[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) -> Result<()>;
/// Creates an in-kernel interrupt controller.
fn create_irq_chip(&self) -> Result<()>;
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
/// 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>>;
/// Registers an event to be signaled whenever a certain address is written to.
fn register_ioevent(
&self,
fd: &EventFd,
addr: &IoEventAddress,
datamatch: Option<DataMatch>,
) -> Result<()>;
/// Unregister an event from a certain address it has been previously registered to.
fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>;
/// Sets the GSI routing table entries, overwriting any previously set
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> Result<()>;
/// Creates a memory region structure that can be used with set_user_memory_region
fn make_user_memory_region(
&self,
slot: u32,
guest_phys_addr: u64,
memory_size: u64,
userspace_addr: u64,
readonly: bool,
) -> MemoryRegion;
/// Creates/modifies a guest physical memory slot.
fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
/// 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.
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>;
/// Enable split Irq capability
#[cfg(target_arch = "x86_64")]
fn enable_split_irq(&self) -> Result<()>;
/// Retrieve guest clock.
#[cfg(target_arch = "x86_64")]
fn get_clock(&self) -> Result<ClockData>;
/// Set guest clock.
#[cfg(target_arch = "x86_64")]
fn set_clock(&self, data: &ClockData) -> Result<()>;
/// Checks if a particular `Cap` is available.
fn check_extension(&self, c: Cap) -> bool;
/// Create a device that is used for passthrough
fn create_passthrough_device(&self) -> Result<Arc<dyn Device>>;
}

View File

@@ -4,14 +4,18 @@ version = "0.1.0"
authors = ["The Chromium OS Authors"]
[dependencies]
libc = "0.2.71"
rand = "0.7.3"
serde = "1.0.111"
vmm-sys-util = ">=0.3.1"
epoll = ">=4.0.1"
libc = "0.2.74"
log = "0.4.11"
net_gen = { path = "../net_gen" }
rand = "0.7.3"
serde = "1.0.114"
virtio-bindings = "0.1.0"
vm-memory = { version = "0.2.1", 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.55"
serde_json = "1.0.57"

View File

@@ -11,21 +11,28 @@
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate log;
extern crate net_gen;
extern crate rand;
extern crate serde;
extern crate net_gen;
extern crate virtio_bindings;
extern crate vm_memory;
extern crate vm_virtio;
extern crate vmm_sys_util;
mod mac;
mod open_tap;
mod queue_pair;
mod tap;
use std::io::Error as IoError;
use std::mem;
use std::net;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::{FromRawFd, RawFd};
use std::{io, mem, net};
pub use mac::{MacAddr, MAC_ADDR_LEN};
pub use open_tap::{open_tap, Error as OpenTapError};
pub use queue_pair::{NetCounters, NetQueuePair, NetQueuePairError, RxVirtio, TxVirtio};
pub use tap::{Error as TapError, Tap};
#[derive(Debug)]
@@ -62,6 +69,39 @@ fn create_socket() -> Result<net::UdpSocket> {
Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
}
fn vnet_hdr_len() -> usize {
use virtio_bindings::bindings::virtio_net::virtio_net_hdr_v1;
std::mem::size_of::<virtio_net_hdr_v1>()
}
pub fn register_listener(
epoll_fd: RawFd,
fd: RawFd,
ev_type: epoll::Events,
data: u64,
) -> std::result::Result<(), io::Error> {
epoll::ctl(
epoll_fd,
epoll::ControlOptions::EPOLL_CTL_ADD,
fd,
epoll::Event::new(ev_type, data),
)
}
pub fn unregister_listener(
epoll_fd: RawFd,
fd: RawFd,
ev_type: epoll::Events,
data: u64,
) -> std::result::Result<(), io::Error> {
epoll::ctl(
epoll_fd,
epoll::ControlOptions::EPOLL_CTL_DEL,
fd,
epoll::Event::new(ev_type, data),
)
}
#[cfg(test)]
mod tests {
use super::*;

116
net_util/src/open_tap.rs Normal file
View File

@@ -0,0 +1,116 @@
// Copyright (c) 2020 Intel Corporation. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use super::{vnet_hdr_len, MacAddr, Tap, TapError};
use std::net::Ipv4Addr;
use std::path::Path;
use std::{fs, io};
#[derive(Debug)]
pub enum Error {
/// Failed to convert an hexadecimal string into an integer.
ConvertHexStringToInt(std::num::ParseIntError),
/// Error related to the multiqueue support (no support TAP side).
MultiQueueNoTapSupport,
/// Error related to the multiqueue support (no support device side).
MultiQueueNoDeviceSupport,
/// Failed to read the TAP flags from sysfs.
ReadSysfsTunFlags(io::Error),
/// Open tap device failed.
TapOpen(TapError),
/// Setting tap IP failed.
TapSetIp(TapError),
/// Setting tap netmask failed.
TapSetNetmask(TapError),
/// Setting MAC address failed
TapSetMac(TapError),
/// Getting MAC address failed
TapGetMac(TapError),
/// Setting tap interface offload flags failed.
TapSetOffload(TapError),
/// Setting vnet header size failed.
TapSetVnetHdrSize(TapError),
/// Enabling tap interface failed.
TapEnable(TapError),
}
type Result<T> = std::result::Result<T, Error>;
fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> {
if let Some(tap_name) = if_name {
let mq = queue_pairs > 1;
let path = format!("/sys/class/net/{}/tun_flags", tap_name);
// interface does not exist, check is not required
if !Path::new(&path).exists() {
return Ok(());
}
let tun_flags_str = fs::read_to_string(path).map_err(Error::ReadSysfsTunFlags)?;
let tun_flags = u32::from_str_radix(tun_flags_str.trim().trim_start_matches("0x"), 16)
.map_err(Error::ConvertHexStringToInt)?;
if (tun_flags & net_gen::IFF_MULTI_QUEUE != 0) && !mq {
return Err(Error::MultiQueueNoDeviceSupport);
} else if (tun_flags & net_gen::IFF_MULTI_QUEUE == 0) && mq {
return Err(Error::MultiQueueNoTapSupport);
}
}
Ok(())
}
/// Create a new virtio network device with the given IP address and
/// netmask.
pub fn open_tap(
if_name: Option<&str>,
ip_addr: Option<Ipv4Addr>,
netmask: Option<Ipv4Addr>,
host_mac: &mut Option<MacAddr>,
num_rx_q: usize,
) -> Result<Vec<Tap>> {
let mut taps: Vec<Tap> = Vec::new();
let mut ifname: String = String::new();
let vnet_hdr_size = vnet_hdr_len() as i32;
let flag = net_gen::TUN_F_CSUM | net_gen::TUN_F_UFO | net_gen::TUN_F_TSO4 | net_gen::TUN_F_TSO6;
// In case the tap interface already exists, check if the number of
// queues is appropriate. The tap might not support multiqueue while
// the number of queues indicates the user expects multiple queues, or
// on the contrary, the tap might support multiqueue while the number
// of queues indicates the user doesn't expect multiple queues.
check_mq_support(&if_name, num_rx_q)?;
for i in 0..num_rx_q {
let tap: Tap;
if i == 0 {
tap = match if_name {
Some(name) => Tap::open_named(name, num_rx_q).map_err(Error::TapOpen)?,
None => Tap::new(num_rx_q).map_err(Error::TapOpen)?,
};
if let Some(ip) = ip_addr {
tap.set_ip_addr(ip).map_err(Error::TapSetIp)?;
}
if let Some(mask) = netmask {
tap.set_netmask(mask).map_err(Error::TapSetNetmask)?;
}
if let Some(mac) = host_mac {
tap.set_mac_addr(*mac).map_err(Error::TapSetMac)?
} else {
*host_mac = Some(tap.get_mac_addr().map_err(Error::TapGetMac)?)
}
tap.enable().map_err(Error::TapEnable)?;
tap.set_offload(flag).map_err(Error::TapSetOffload)?;
tap.set_vnet_hdr_size(vnet_hdr_size)
.map_err(Error::TapSetVnetHdrSize)?;
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.set_offload(flag).map_err(Error::TapSetOffload)?;
tap.set_vnet_hdr_size(vnet_hdr_size)
.map_err(Error::TapSetVnetHdrSize)?;
}
taps.push(tap);
}
Ok(taps)
}

380
net_util/src/queue_pair.rs Normal file
View File

@@ -0,0 +1,380 @@
// Copyright (c) 2020 Intel Corporation. All rights reserved.
//
// 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 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;
#[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>,
}
impl Default for TxVirtio {
fn default() -> Self {
Self::new()
}
}
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) {
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();
while let Some(desc) = next_desc {
if desc.is_write_only() {
break;
}
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;
}
}
}
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);
}
}
}
#[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>,
}
impl Default for RxVirtio {
fn default() -> Self {
Self::new()
}
}
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),
}
}
pub fn process_desc_chain(
&mut self,
mem: &GuestMemoryMmap,
mut next_desc: Option<DescriptorChain>,
queue: &mut Queue,
) -> bool {
let head_index = next_desc.as_ref().unwrap().index;
let mut write_count = 0;
// 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);
match write_result {
Ok(_) => {
write_count = limit;
}
Err(e) => {
error!("Failed to write slice: {:?}", e);
break;
}
};
if write_count >= self.bytes_read {
break;
}
next_desc = desc.next_descriptor();
}
None => {
warn!("Receiving buffer is too small to hold frame of current size");
break;
}
}
}
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
}
}
}
#[derive(Default, Clone)]
pub struct NetCounters {
pub tx_bytes: Arc<AtomicU64>,
pub tx_frames: Arc<AtomicU64>,
pub rx_bytes: Arc<AtomicU64>,
pub rx_frames: Arc<AtomicU64>,
}
#[derive(Debug)]
pub enum NetQueuePairError {
/// No memory configured
NoMemoryConfigured,
/// Error registering listener
RegisterListener(io::Error),
/// Error unregistering listener
UnregisterListener(io::Error),
/// Error reading from the TAP device
FailedReadTap,
}
pub struct NetQueuePair {
pub mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
pub tap: Tap,
pub rx: RxVirtio,
pub tx: TxVirtio,
pub epoll_fd: Option<RawFd>,
pub rx_tap_listening: bool,
pub counters: NetCounters,
pub tap_event_id: u16,
}
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.counters
.tx_bytes
.fetch_add(self.tx.counter_bytes.0, Ordering::AcqRel);
self.counters
.tx_frames
.fetch_add(self.tx.counter_frames.0, Ordering::AcqRel);
self.tx.counter_bytes = Wrapping(0);
self.tx.counter_frames = Wrapping(0);
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)
}
}
fn read_tap(&mut self) -> io::Result<usize> {
self.tap.read(&mut self.rx.frame_buf)
}
}

5
option_parser/Cargo.toml Normal file
View File

@@ -0,0 +1,5 @@
[package]
name = "option_parser"
version = "0.1.0"
authors = ["The Cloud Hypervisor Authors"]
edition = "2018"

172
option_parser/src/lib.rs Normal file
View File

@@ -0,0 +1,172 @@
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
#[derive(Default)]
pub struct OptionParser {
options: HashMap<String, OptionParserValue>,
}
struct OptionParserValue {
value: Option<String>,
requires_value: bool,
}
#[derive(Debug)]
pub enum OptionParserError {
UnknownOption(String),
InvalidSyntax(String),
Conversion(String, String),
}
impl fmt::Display for OptionParserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
OptionParserError::UnknownOption(s) => write!(f, "unknown option: {}", s),
OptionParserError::InvalidSyntax(s) => write!(f, "invalid syntax:{}", s),
OptionParserError::Conversion(field, value) => {
write!(f, "unable to parse {} for {}", value, field)
}
}
}
}
type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
impl OptionParser {
pub fn new() -> Self {
Self {
options: HashMap::new(),
}
}
pub fn parse(&mut self, input: &str) -> OptionParserResult<()> {
if input.trim().is_empty() {
return Ok(());
}
let options_list: Vec<&str> = input.trim().split(',').collect();
for option in options_list.iter() {
let parts: Vec<&str> = option.split('=').collect();
match self.options.get_mut(parts[0]) {
None => return Err(OptionParserError::UnknownOption(parts[0].to_owned())),
Some(value) => {
if value.requires_value {
if parts.len() != 2 {
return Err(OptionParserError::InvalidSyntax((*option).to_owned()));
}
value.value = Some(parts[1].trim().to_owned());
} else {
value.value = Some(String::new());
}
}
}
}
Ok(())
}
pub fn add(&mut self, option: &str) -> &mut Self {
self.options.insert(
option.to_owned(),
OptionParserValue {
value: None,
requires_value: true,
},
);
self
}
pub fn add_valueless(&mut self, option: &str) -> &mut Self {
self.options.insert(
option.to_owned(),
OptionParserValue {
value: None,
requires_value: false,
},
);
self
}
pub fn get(&self, option: &str) -> Option<String> {
self.options
.get(option)
.and_then(|v| v.value.clone())
.and_then(|s| if s.is_empty() { None } else { Some(s) })
}
pub fn is_set(&self, option: &str) -> bool {
self.options
.get(option)
.and_then(|v| v.value.as_ref())
.is_some()
}
pub fn convert<T: FromStr>(&self, option: &str) -> OptionParserResult<Option<T>> {
match self.get(option) {
None => Ok(None),
Some(v) => Ok(Some(v.parse().map_err(|_| {
OptionParserError::Conversion(option.to_owned(), v.to_owned())
})?)),
}
}
}
pub struct Toggle(pub bool);
pub enum ToggleParseError {
InvalidValue(String),
}
impl FromStr for Toggle {
type Err = ToggleParseError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"" => Ok(Toggle(false)),
"on" => Ok(Toggle(true)),
"off" => Ok(Toggle(false)),
"true" => Ok(Toggle(true)),
"false" => Ok(Toggle(false)),
_ => Err(ToggleParseError::InvalidValue(s.to_owned())),
}
}
}
pub struct ByteSized(pub u64);
pub enum ByteSizedParseError {
InvalidValue(String),
}
impl FromStr for ByteSized {
type Err = ByteSizedParseError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(ByteSized({
let s = s.trim();
let shift = if s.ends_with('K') {
10
} else if s.ends_with('M') {
20
} else if s.ends_with('G') {
30
} else {
0
};
let s = s.trim_end_matches(|c| c == 'K' || c == 'M' || c == 'G');
s.parse::<u64>()
.map_err(|_| ByteSizedParseError::InvalidValue(s.to_owned()))?
<< shift
}))
}
}

View File

@@ -8,12 +8,11 @@ edition = "2018"
anyhow = "1.0"
byteorder = "1.3.4"
devices = { path = "../devices" }
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch" }
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
hypervisor = { path = "../hypervisor" }
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
vmm-sys-util = ">=0.3.1"
libc = "0.2.71"
log = "0.4.8"
libc = "0.2.74"
log = "0.4.11"
serde = {version = ">=1.0.27", features = ["rc"] }
serde_derive = ">=1.0.27"
serde_json = ">=1.0.9"
@@ -23,5 +22,5 @@ vm-memory = "0.2.1"
vm-migration = { path = "../vm-migration" }
[dependencies.vfio-bindings]
git = "https://github.com/rust-vmm/vfio-bindings"
version = "0.2.0"
features = ["fam-wrappers"]

View File

@@ -110,16 +110,19 @@ impl PciBus {
pub fn register_mapping(
&self,
dev: Arc<Mutex<dyn BusDevice>>,
io_bus: &devices::Bus,
#[cfg(target_arch = "x86_64")] io_bus: &devices::Bus,
mmio_bus: &devices::Bus,
bars: Vec<(GuestAddress, GuestUsize, PciBarRegionType)>,
) -> Result<()> {
for (address, size, type_) in bars {
match type_ {
PciBarRegionType::IORegion => {
#[cfg(target_arch = "x86_64")]
io_bus
.insert(dev.clone(), address.raw_value(), size)
.map_err(PciRootError::PioInsert)?;
#[cfg(target_arch = "aarch64")]
error!("I/O region is not supported");
}
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
mmio_bus

View File

@@ -277,14 +277,19 @@ fn decode_64_bits_bar_size(bar_size_hi: u32, bar_size_lo: u32) -> Option<u64> {
None
}
#[derive(Serialize, Deserialize, Default, Clone, Copy)]
struct PciBar {
addr: u32,
size: u32,
used: bool,
r#type: Option<PciBarRegionType>,
}
#[derive(Serialize, Deserialize)]
struct PciConfigurationState {
registers: Vec<u32>,
writable_bits: Vec<u32>,
bar_addr: Vec<u32>,
bar_size: Vec<u32>,
bar_used: Vec<bool>,
bar_type: Vec<Option<PciBarRegionType>>,
bars: Vec<PciBar>,
rom_bar_addr: u32,
rom_bar_size: u32,
rom_bar_used: bool,
@@ -298,10 +303,7 @@ struct PciConfigurationState {
pub struct PciConfiguration {
registers: [u32; NUM_CONFIGURATION_REGISTERS],
writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register.
bar_addr: [u32; NUM_BAR_REGS],
bar_size: [u32; NUM_BAR_REGS],
bar_used: [bool; NUM_BAR_REGS],
bar_type: [Option<PciBarRegionType>; NUM_BAR_REGS],
bars: [PciBar; NUM_BAR_REGS],
rom_bar_addr: u32,
rom_bar_size: u32,
rom_bar_used: bool,
@@ -404,8 +406,6 @@ impl PciConfiguration {
) -> Self {
let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
let bar_addr = [0u32; NUM_BAR_REGS];
let bar_size = [0u32; NUM_BAR_REGS];
registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
// TODO(dverkamp): Status should be write-1-to-clear
writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
@@ -432,13 +432,12 @@ impl PciConfiguration {
};
registers[11] = u32::from(subsystem_id) << 16 | u32::from(subsystem_vendor_id);
let bars = [PciBar::default(); NUM_BAR_REGS];
PciConfiguration {
registers,
writable_bits,
bar_addr,
bar_size,
bar_used: [false; NUM_BAR_REGS],
bar_type: [None; NUM_BAR_REGS],
bars,
rom_bar_addr: 0,
rom_bar_size: 0,
rom_bar_used: false,
@@ -452,10 +451,7 @@ impl PciConfiguration {
PciConfigurationState {
registers: self.registers.to_vec(),
writable_bits: self.writable_bits.to_vec(),
bar_addr: self.bar_addr.to_vec(),
bar_size: self.bar_size.to_vec(),
bar_used: self.bar_used.to_vec(),
bar_type: self.bar_type.to_vec(),
bars: self.bars.to_vec(),
rom_bar_addr: self.rom_bar_addr,
rom_bar_size: self.rom_bar_size,
rom_bar_used: self.rom_bar_used,
@@ -468,10 +464,7 @@ impl PciConfiguration {
self.registers.clone_from_slice(state.registers.as_slice());
self.writable_bits
.clone_from_slice(state.writable_bits.as_slice());
self.bar_addr.clone_from_slice(state.bar_addr.as_slice());
self.bar_size.clone_from_slice(state.bar_size.as_slice());
self.bar_used.clone_from_slice(state.bar_used.as_slice());
self.bar_type.clone_from_slice(state.bar_type.as_slice());
self.bars.clone_from_slice(state.bars.as_slice());
self.rom_bar_addr = state.rom_bar_addr;
self.rom_bar_size = state.rom_bar_size;
self.rom_bar_used = state.rom_bar_used;
@@ -492,7 +485,7 @@ impl PciConfiguration {
// 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 {
mask &= self.bar_size[reg_idx - 4];
mask &= self.bars[reg_idx - 4].size;
}
} else if reg_idx == ROM_BAR_REG {
// Handle very specific case where the BAR is being written with
@@ -561,7 +554,7 @@ impl PciConfiguration {
/// (i.e, region size must be power of two, register not already used). Returns 'None' on
/// failure all, `Some(BarIndex)` on success.
pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result<usize> {
if self.bar_used[config.reg_idx] {
if self.bars[config.reg_idx].used {
return Err(Error::BarInUse(config.reg_idx));
}
@@ -586,7 +579,7 @@ impl PciConfiguration {
// Encode the BAR size as expected by the software running in
// the guest.
self.bar_size[config.reg_idx] =
self.bars[config.reg_idx].size =
encode_32_bits_bar_size(config.size as u32).ok_or(Error::Encode32BarSize)?;
}
PciBarRegionType::Memory64BitRegion => {
@@ -598,7 +591,7 @@ impl PciConfiguration {
return Err(Error::BarAddressInvalid(config.addr, config.size));
}
if self.bar_used[config.reg_idx + 1] {
if self.bars[config.reg_idx + 1].used {
return Err(Error::BarInUse64(config.reg_idx));
}
@@ -609,10 +602,10 @@ impl PciConfiguration {
self.registers[bar_idx + 1] = (config.addr >> 32) as u32;
self.writable_bits[bar_idx + 1] = 0xffff_ffff;
self.bar_addr[config.reg_idx + 1] = self.registers[bar_idx + 1];
self.bar_size[config.reg_idx] = bar_size_lo;
self.bar_size[config.reg_idx + 1] = bar_size_hi;
self.bar_used[config.reg_idx + 1] = true;
self.bars[config.reg_idx + 1].addr = self.registers[bar_idx + 1];
self.bars[config.reg_idx].size = bar_size_lo;
self.bars[config.reg_idx + 1].size = bar_size_hi;
self.bars[config.reg_idx + 1].used = true;
}
}
@@ -626,9 +619,9 @@ impl PciConfiguration {
self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
self.writable_bits[bar_idx] = mask;
self.bar_addr[config.reg_idx] = self.registers[bar_idx];
self.bar_used[config.reg_idx] = true;
self.bar_type[config.reg_idx] = Some(config.region_type);
self.bars[config.reg_idx].addr = self.registers[bar_idx];
self.bars[config.reg_idx].used = true;
self.bars[config.reg_idx].r#type = Some(config.region_type);
Ok(config.reg_idx)
}
@@ -668,11 +661,11 @@ impl PciConfiguration {
pub fn get_bar_addr(&self, bar_num: usize) -> u64 {
let bar_idx = BAR0_REG + bar_num;
let mut addr = u64::from(self.bar_addr[bar_num] & self.writable_bits[bar_idx]);
let mut addr = u64::from(self.bars[bar_num].addr & self.writable_bits[bar_idx]);
if let Some(bar_type) = self.bar_type[bar_num] {
if let Some(bar_type) = self.bars[bar_num].r#type {
if bar_type == PciBarRegionType::Memory64BitRegion {
addr |= u64::from(self.bar_addr[bar_num + 1]) << 32;
addr |= u64::from(self.bars[bar_num + 1].addr) << 32;
}
}
@@ -777,11 +770,11 @@ impl PciConfiguration {
let mask = self.writable_bits[reg_idx];
if reg_idx >= BAR0_REG && reg_idx < BAR0_REG + NUM_BAR_REGS {
let bar_idx = reg_idx - 4;
if (value & mask) != (self.bar_addr[bar_idx] & mask) {
if (value & mask) != (self.bars[bar_idx].addr & mask) {
// Handle special case where the address being written is
// different from the address initially provided. This is a
// BAR reprogramming case which needs to be properly caught.
if let Some(bar_type) = self.bar_type[bar_idx] {
if let Some(bar_type) = self.bars[bar_idx].r#type {
match bar_type {
PciBarRegionType::Memory64BitRegion => {}
_ => {
@@ -795,16 +788,16 @@ impl PciConfiguration {
"DETECT BAR REPROG: current 0x{:x}, new 0x{:x}",
self.registers[reg_idx], value
);
let old_base = u64::from(self.bar_addr[bar_idx] & mask);
let old_base = u64::from(self.bars[bar_idx].addr & mask);
let new_base = u64::from(value & mask);
let len = u64::from(
decode_32_bits_bar_size(self.bar_size[bar_idx])
decode_32_bits_bar_size(self.bars[bar_idx].size)
.ok_or(Error::Decode32BarSize)
.unwrap(),
);
let region_type = bar_type;
self.bar_addr[bar_idx] = value;
self.bars[bar_idx].addr = value;
return Some(BarReprogrammingParams {
old_base,
@@ -816,7 +809,7 @@ impl PciConfiguration {
}
} else if (reg_idx > BAR0_REG)
&& (self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1])
!= (self.bar_addr[bar_idx - 1] & self.writable_bits[reg_idx - 1])
!= (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1])
{
// Ignore the case where the BAR size is being asked for.
// Because we are in the 64bits case here, we have to check
@@ -824,7 +817,7 @@ impl PciConfiguration {
// asked for the BAR size too.
if value == 0xffff_ffff
&& self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1]
== self.bar_size[bar_idx - 1] & self.writable_bits[reg_idx - 1]
== self.bars[bar_idx - 1].size & self.writable_bits[reg_idx - 1]
{
return None;
}
@@ -833,18 +826,20 @@ impl PciConfiguration {
"DETECT BAR REPROG: current 0x{:x}, new 0x{:x}",
self.registers[reg_idx], value
);
let old_base = u64::from(self.bar_addr[bar_idx] & mask) << 32
| u64::from(self.bar_addr[bar_idx - 1] & self.writable_bits[reg_idx - 1]);
let old_base = u64::from(self.bars[bar_idx].addr & mask) << 32
| u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
let new_base = u64::from(value & mask) << 32
| u64::from(self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1]);
let len =
decode_64_bits_bar_size(self.bar_size[bar_idx], self.bar_size[bar_idx - 1])
.ok_or(Error::Decode64BarSize)
.unwrap();
let len = decode_64_bits_bar_size(
self.bars[bar_idx].size,
self.bars[bar_idx - 1].size,
)
.ok_or(Error::Decode64BarSize)
.unwrap();
let region_type = PciBarRegionType::Memory64BitRegion;
self.bar_addr[bar_idx] = value;
self.bar_addr[bar_idx - 1] = self.registers[reg_idx - 1];
self.bars[bar_idx].addr = value;
self.bars[bar_idx - 1].addr = self.registers[reg_idx - 1];
return Some(BarReprogrammingParams {
old_base,

View File

@@ -6,6 +6,7 @@
#[macro_use]
extern crate log;
extern crate devices;
extern crate hypervisor;
extern crate serde;
#[macro_use]
extern crate serde_derive;

View File

@@ -201,6 +201,7 @@ impl MsiConfig {
high_addr: self.cap.msg_addr_hi,
low_addr: self.cap.msg_addr_lo,
data: self.cap.msg_data as u32,
devid: 0,
};
if let Err(e) = self

View File

@@ -72,6 +72,7 @@ struct MsixConfigState {
pub struct MsixConfig {
pub table_entries: Vec<MsixTableEntry>,
pub pba_entries: Vec<u64>,
pub devid: u32,
interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
masked: bool,
enabled: bool,
@@ -81,6 +82,7 @@ impl MsixConfig {
pub fn new(
msix_vectors: u16,
interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
devid: u32,
) -> Self {
assert!(msix_vectors <= MAX_MSIX_VECTORS_PER_DEVICE);
@@ -93,6 +95,7 @@ impl MsixConfig {
MsixConfig {
table_entries,
pba_entries,
devid,
interrupt_source_group,
masked: false,
enabled: false,
@@ -124,6 +127,7 @@ impl MsixConfig {
high_addr: table_entry.msg_addr_hi,
low_addr: table_entry.msg_addr_lo,
data: table_entry.msg_data,
devid: self.devid,
};
self.interrupt_source_group
@@ -162,6 +166,7 @@ impl MsixConfig {
high_addr: table_entry.msg_addr_hi,
low_addr: table_entry.msg_addr_lo,
data: table_entry.msg_data,
devid: self.devid,
};
if let Err(e) = self
@@ -306,6 +311,7 @@ impl MsixConfig {
high_addr: table_entry.msg_addr_hi,
low_addr: table_entry.msg_addr_lo,
data: table_entry.msg_data,
devid: self.devid,
};
if let Err(e) = self.interrupt_source_group.update(

View File

@@ -13,8 +13,6 @@ use crate::{
};
use byteorder::{ByteOrder, LittleEndian};
use devices::BusDevice;
use kvm_bindings::kvm_userspace_memory_region;
use kvm_ioctls::*;
use std::any::Any;
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
@@ -38,10 +36,10 @@ pub enum VfioPciError {
AllocateGsi,
EventFd(io::Error),
InterruptSourceGroupCreate(io::Error),
IrqFd(kvm_ioctls::Error),
IrqFd(hypervisor::HypervisorVmError),
NewVfioPciDevice,
MapRegionGuest(kvm_ioctls::Error),
SetGsiRouting(kvm_ioctls::Error),
MapRegionGuest(anyhow::Error),
SetGsiRouting(hypervisor::HypervisorVmError),
MsiNotConfigured,
MsixNotConfigured,
UpdateMemory(VfioError),
@@ -281,7 +279,7 @@ impl VfioPciConfig {
/// The VMM creates a VfioDevice, then assigns it to a VfioPciDevice,
/// which then gets added to the PCI bus.
pub struct VfioPciDevice {
vm_fd: Arc<VmFd>,
vm: Arc<dyn hypervisor::Vm>,
device: Arc<VfioDevice>,
vfio_pci_configuration: VfioPciConfig,
configuration: PciConfiguration,
@@ -293,7 +291,7 @@ pub struct VfioPciDevice {
impl VfioPciDevice {
/// Constructs a new Vfio Pci device for the given Vfio device
pub fn new(
vm_fd: &Arc<VmFd>,
vm: &Arc<dyn hypervisor::Vm>,
device: VfioDevice,
interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
mem: GuestMemoryAtomic<GuestMemoryMmap>,
@@ -317,7 +315,7 @@ impl VfioPciDevice {
let vfio_pci_configuration = VfioPciConfig::new(Arc::clone(&device));
let mut vfio_pci_device = VfioPciDevice {
vm_fd: vm_fd.clone(),
vm: vm.clone(),
device,
configuration,
vfio_pci_configuration,
@@ -364,7 +362,7 @@ impl VfioPciDevice {
})
.unwrap();
let msix_config = MsixConfig::new(msix_cap.table_size(), interrupt_source_group.clone());
let msix_config = MsixConfig::new(msix_cap.table_size(), interrupt_source_group.clone(), 0);
self.interrupt.msix = Some(VfioMsix {
bar: msix_config,
@@ -512,7 +510,7 @@ impl VfioPciDevice {
/// # Return value
///
/// This function returns the updated KVM memory slot id.
pub fn map_mmio_regions<F>(&mut self, vm: &Arc<VmFd>, mem_slot: F) -> Result<()>
pub fn map_mmio_regions<F>(&mut self, vm: &Arc<dyn hypervisor::Vm>, mem_slot: F) -> Result<()>
where
F: Fn() -> u32,
{
@@ -561,19 +559,16 @@ impl VfioPciDevice {
}
let slot = mem_slot();
let mem_region = kvm_userspace_memory_region {
let mem_region = vm.make_user_memory_region(
slot,
guest_phys_addr: region.start.raw_value() + mmap_offset,
memory_size: mmap_size as u64,
userspace_addr: host_addr as u64,
flags: 0,
};
region.start.raw_value() + mmap_offset,
mmap_size as u64,
host_addr as u64,
false,
);
// Safe because the guest regions are guaranteed not to overlap.
unsafe {
vm.set_user_memory_region(mem_region)
.map_err(VfioPciError::MapRegionGuest)?;
}
vm.set_user_memory_region(mem_region)
.map_err(|e| VfioPciError::MapRegionGuest(e.into()))?;
// Update the region with memory mapped info.
region.mem_slot = Some(slot);
@@ -593,15 +588,15 @@ impl VfioPciDevice {
let (mmap_offset, _) = self.device.get_region_mmap(region.index);
// Remove region from KVM
let kvm_region = kvm_userspace_memory_region {
slot: mem_slot,
guest_phys_addr: region.start.raw_value() + mmap_offset,
memory_size: 0,
userspace_addr: host_addr,
flags: 0,
};
// Safe because the guest regions are guaranteed not to overlap.
if let Err(e) = unsafe { self.vm_fd.set_user_memory_region(kvm_region) } {
let kvm_region = self.vm.make_user_memory_region(
mem_slot,
region.start.raw_value() + mmap_offset,
0,
host_addr as u64,
false,
);
if let Err(e) = self.vm.set_user_memory_region(kvm_region) {
error!(
"Could not remove the userspace memory region from KVM: {}",
e
@@ -645,7 +640,7 @@ impl Drop for VfioPciDevice {
if self
.device
.setup_dma_map(self.mem.memory().deref())
.unset_dma_map(self.mem.memory().deref())
.is_err()
{
error!("failed to remove all guest memory regions from iommu table");
@@ -748,20 +743,25 @@ impl PciDevice for VfioPciDevice {
let mut region_type = PciBarRegionType::Memory32BitRegion;
if io_bar {
// IO BAR
region_type = PciBarRegionType::IORegion;
#[cfg(target_arch = "x86_64")]
{
// IO BAR
region_type = PciBarRegionType::IORegion;
// Clear first bit.
lsb_size &= 0xffff_fffc;
// Clear first bit.
lsb_size &= 0xffff_fffc;
// Find the first bit that's set to 1.
let first_bit = lsb_size.trailing_zeros();
region_size = 2u64.pow(first_bit);
// We need to allocate a guest PIO address range for that BAR.
// The address needs to be 4 bytes aligned.
bar_addr = allocator
.allocate_io_addresses(None, region_size, Some(0x4))
.ok_or_else(|| PciDeviceError::IoAllocationFailed(region_size))?;
// Find the first bit that's set to 1.
let first_bit = lsb_size.trailing_zeros();
region_size = 2u64.pow(first_bit);
// We need to allocate a guest PIO address range for that BAR.
// The address needs to be 4 bytes aligned.
bar_addr = allocator
.allocate_io_addresses(None, region_size, Some(0x4))
.ok_or_else(|| PciDeviceError::IoAllocationFailed(region_size))?;
}
#[cfg(target_arch = "aarch64")]
unimplemented!()
} else {
if is_64bit_bar {
// 64 bits Memory BAR
@@ -871,7 +871,10 @@ impl PciDevice for VfioPciDevice {
for region in self.mmio_regions.iter() {
match region.type_ {
PciBarRegionType::IORegion => {
#[cfg(target_arch = "x86_64")]
allocator.free_io_addresses(region.start, region.length);
#[cfg(target_arch = "aarch64")]
error!("I/O region is not supported");
}
PciBarRegionType::Memory32BitRegion => {
allocator.free_mmio_hole_addresses(region.start, region.length);
@@ -1013,34 +1016,30 @@ impl PciDevice for VfioPciDevice {
let (mmap_offset, mmap_size) = self.device.get_region_mmap(region.index);
// Remove old region from KVM
let old_mem_region = kvm_userspace_memory_region {
slot: mem_slot,
guest_phys_addr: old_base + mmap_offset,
memory_size: 0,
userspace_addr: host_addr,
flags: 0,
};
// Safe because the guest regions are guaranteed not to overlap.
unsafe {
self.vm_fd
.set_user_memory_region(old_mem_region)
.map_err(|e| io::Error::from_raw_os_error(e.errno()))?;
}
let old_mem_region = self.vm.make_user_memory_region(
mem_slot,
old_base + mmap_offset,
0,
host_addr as u64,
false,
);
self.vm
.set_user_memory_region(old_mem_region)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
// Insert new region to KVM
let new_mem_region = kvm_userspace_memory_region {
slot: mem_slot,
guest_phys_addr: new_base + mmap_offset,
memory_size: mmap_size as u64,
userspace_addr: host_addr,
flags: 0,
};
// Safe because the guest regions are guaranteed not to overlap.
unsafe {
self.vm_fd
.set_user_memory_region(new_mem_region)
.map_err(|e| io::Error::from_raw_os_error(e.errno()))?;
}
let new_mem_region = self.vm.make_user_memory_region(
mem_slot,
new_base + mmap_offset,
mmap_size as u64,
host_addr as u64,
false,
);
self.vm
.set_user_memory_region(new_mem_region)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
}
}
}

View File

@@ -10,11 +10,10 @@ path = "src/qcow.rs"
[dependencies]
byteorder = "1.3.4"
libc = "0.2.71"
log = "0.4.8"
remain = "0.2.1"
libc = "0.2.74"
log = "0.4.11"
remain = "0.2.2"
vmm-sys-util = ">=0.3.1"
vm-virtio = { path = "../vm-virtio" }
[dev-dependencies]
tempfile = "3.1.0"

View File

@@ -6,26 +6,26 @@
extern crate log;
mod qcow_raw_file;
mod raw_file;
mod refcount;
mod vec_cache;
use crate::qcow_raw_file::QcowRawFile;
use crate::refcount::RefCount;
use crate::vec_cache::{CacheMap, Cacheable, VecCache};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use libc::{EINVAL, ENOSPC, ENOTSUP};
use remain::sorted;
use vm_virtio::RawFile;
use std::cmp::{max, min};
use std::fmt::{self, Display};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem::size_of;
use vmm_sys_util::{
file_traits::FileSetLen, file_traits::FileSync, seek_hole::SeekHole, write_zeroes::PunchHole,
write_zeroes::WriteZeroes,
};
use std::cmp::{max, min};
use std::fmt::{self, Display};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem::size_of;
use crate::qcow_raw_file::QcowRawFile;
use crate::refcount::RefCount;
use crate::vec_cache::{CacheMap, Cacheable, VecCache};
pub use crate::raw_file::RawFile;
#[sorted]
#[derive(Debug)]
@@ -367,8 +367,7 @@ fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u
///
/// ```
/// # use std::io::{Read, Seek, SeekFrom};
/// # use vm_virtio::RawFile;
/// # use qcow::{self, QcowFile};
/// # use qcow::{self, QcowFile, RawFile};
/// # fn test(file: std::fs::File) -> std::io::Result<()> {
/// let mut raw_img = RawFile::new(file, false);
/// let mut q = QcowFile::from(raw_img).expect("Can't open qcow file");
@@ -1703,7 +1702,6 @@ mod tests {
use super::*;
use std::io::{Read, Seek, SeekFrom, Write};
use tempfile::tempfile;
use vm_virtio::RawFile;
fn valid_header_v3() -> Vec<u8> {
vec![

View File

@@ -2,11 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use super::RawFile;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{self, BufWriter, Seek, SeekFrom};
use std::mem::size_of;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use vm_virtio::RawFile;
use vmm_sys_util::write_zeroes::WriteZeroes;
/// A qcow file. Allows reading/writing clusters and appending clusters.

324
qcow/src/raw_file.rs Normal file
View File

@@ -0,0 +1,324 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use libc::c_void;
use std::alloc::{alloc_zeroed, dealloc, Layout};
use std::convert::TryInto;
use std::fs::{File, Metadata};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::os::unix::io::{AsRawFd, RawFd};
use std::slice;
use vmm_sys_util::{seek_hole::SeekHole, write_zeroes::PunchHole};
#[derive(Debug)]
pub struct RawFile {
file: File,
alignment: usize,
position: u64,
}
const BLK_ALIGNMENTS: [usize; 2] = [512, 4096];
fn is_valid_alignment(fd: RawFd, alignment: usize) -> bool {
let layout = Layout::from_size_align(alignment, alignment).unwrap();
let ptr = unsafe { alloc_zeroed(layout) };
let ret = unsafe {
::libc::pread(
fd,
ptr as *mut c_void,
alignment,
alignment.try_into().unwrap(),
)
};
unsafe { dealloc(ptr, layout) };
ret >= 0
}
impl RawFile {
pub fn new(file: File, direct_io: bool) -> Self {
// Assume no alignment restrictions if we aren't using O_DIRECT.
let mut alignment = 0;
if direct_io {
for align in &BLK_ALIGNMENTS {
if is_valid_alignment(file.as_raw_fd(), *align) {
alignment = *align;
break;
}
}
}
RawFile {
file,
alignment: alignment.try_into().unwrap(),
position: 0,
}
}
fn round_up(&self, offset: u64) -> u64 {
let align: u64 = self.alignment.try_into().unwrap();
((offset / (align + 1)) + 1) * align
}
fn round_down(&self, offset: u64) -> u64 {
let align: u64 = self.alignment.try_into().unwrap();
(offset / align) * align
}
fn is_aligned(&self, buf: &[u8]) -> bool {
if self.alignment == 0 {
return true;
}
let align64: u64 = self.alignment.try_into().unwrap();
(self.position % align64 == 0)
&& ((buf.as_ptr() as usize) % self.alignment == 0)
&& (buf.len() % self.alignment == 0)
}
pub fn set_len(&self, size: u64) -> std::io::Result<()> {
self.file.set_len(size)
}
pub fn metadata(&self) -> std::io::Result<Metadata> {
self.file.metadata()
}
pub fn try_clone(&self) -> std::io::Result<RawFile> {
Ok(RawFile {
file: self.file.try_clone().expect("RawFile cloning failed"),
alignment: self.alignment,
position: self.position,
})
}
pub fn sync_all(&self) -> std::io::Result<()> {
self.file.sync_all()
}
pub fn sync_data(&self) -> std::io::Result<()> {
self.file.sync_data()
}
}
impl Read for RawFile {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if self.is_aligned(buf) {
match self.file.read(buf) {
Ok(r) => {
self.position = self.position.checked_add(r.try_into().unwrap()).unwrap();
Ok(r)
}
Err(e) => Err(e),
}
} else {
let rounded_pos: u64 = self.round_down(self.position);
let file_offset: usize = self
.position
.checked_sub(rounded_pos)
.unwrap()
.try_into()
.unwrap();
let buf_len: usize = buf.len();
let rounded_len: usize = self
.round_up(
file_offset
.checked_add(buf_len)
.unwrap()
.try_into()
.unwrap(),
)
.try_into()
.unwrap();
let layout = Layout::from_size_align(rounded_len, self.alignment).unwrap();
let tmp_ptr = unsafe { alloc_zeroed(layout) };
let tmp_buf = unsafe { slice::from_raw_parts_mut(tmp_ptr, rounded_len) };
// This can eventually replaced with read_at once its interface
// has been stabilized.
let ret = unsafe {
::libc::pread64(
self.file.as_raw_fd(),
tmp_buf.as_mut_ptr() as *mut c_void,
tmp_buf.len(),
rounded_pos.try_into().unwrap(),
)
};
if ret < 0 {
unsafe { dealloc(tmp_ptr, layout) };
return Err(io::Error::last_os_error());
}
let read: usize = ret.try_into().unwrap();
if read < file_offset {
unsafe { dealloc(tmp_ptr, layout) };
return Ok(0);
}
let mut to_copy = read - file_offset;
if to_copy > buf_len {
to_copy = buf_len;
}
buf.copy_from_slice(&tmp_buf[file_offset..(file_offset + buf_len)]);
unsafe { dealloc(tmp_ptr, layout) };
self.seek(SeekFrom::Current(to_copy.try_into().unwrap()))
.unwrap();
Ok(to_copy.try_into().unwrap())
}
}
}
impl Write for RawFile {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
if self.is_aligned(buf) {
match self.file.write(buf) {
Ok(r) => {
self.position = self.position.checked_add(r.try_into().unwrap()).unwrap();
Ok(r)
}
Err(e) => Err(e),
}
} else {
let rounded_pos: u64 = self.round_down(self.position);
let file_offset: usize = self
.position
.checked_sub(rounded_pos)
.unwrap()
.try_into()
.unwrap();
let buf_len: usize = buf.len();
let rounded_len: usize = self
.round_up(
file_offset
.checked_add(buf_len)
.unwrap()
.try_into()
.unwrap(),
)
.try_into()
.unwrap();
let layout = Layout::from_size_align(rounded_len, self.alignment).unwrap();
let tmp_ptr = unsafe { alloc_zeroed(layout) };
let tmp_buf = unsafe { slice::from_raw_parts_mut(tmp_ptr, rounded_len) };
// This can eventually replaced with read_at once its interface
// has been stabilized.
let ret = unsafe {
::libc::pread64(
self.file.as_raw_fd(),
tmp_buf.as_mut_ptr() as *mut c_void,
tmp_buf.len(),
rounded_pos.try_into().unwrap(),
)
};
if ret < 0 {
unsafe { dealloc(tmp_ptr, layout) };
return Err(io::Error::last_os_error());
};
tmp_buf[file_offset..(file_offset + buf_len)].copy_from_slice(buf);
// This can eventually replaced with write_at once its interface
// has been stabilized.
let ret = unsafe {
::libc::pwrite64(
self.file.as_raw_fd(),
tmp_buf.as_ptr() as *const c_void,
tmp_buf.len(),
rounded_pos.try_into().unwrap(),
)
};
unsafe { dealloc(tmp_ptr, layout) };
if ret < 0 {
return Err(io::Error::last_os_error());
}
let written: usize = ret.try_into().unwrap();
if written < file_offset {
Ok(0)
} else {
let mut to_seek = written - file_offset;
if to_seek > buf_len {
to_seek = buf_len;
}
self.seek(SeekFrom::Current(to_seek.try_into().unwrap()))
.unwrap();
Ok(to_seek.try_into().unwrap())
}
}
}
fn flush(&mut self) -> std::io::Result<()> {
self.file.sync_all()
}
}
impl Seek for RawFile {
fn seek(&mut self, newpos: SeekFrom) -> std::io::Result<u64> {
match self.file.seek(newpos) {
Ok(pos) => {
self.position = pos;
Ok(pos)
}
Err(e) => Err(e),
}
}
}
impl PunchHole for RawFile {
fn punch_hole(&mut self, offset: u64, length: u64) -> std::io::Result<()> {
self.file.punch_hole(offset, length)
}
}
impl SeekHole for RawFile {
fn seek_hole(&mut self, offset: u64) -> std::io::Result<Option<u64>> {
match self.file.seek_hole(offset) {
Ok(pos) => {
if let Some(p) = pos {
self.position = p;
}
Ok(pos)
}
Err(e) => Err(e),
}
}
fn seek_data(&mut self, offset: u64) -> std::io::Result<Option<u64>> {
match self.file.seek_data(offset) {
Ok(pos) => {
if let Some(p) = pos {
self.position = p;
}
Ok(pos)
}
Err(e) => Err(e),
}
}
}
impl Clone for RawFile {
fn clone(&self) -> Self {
RawFile {
file: self.file.try_clone().expect("RawFile cloning failed"),
alignment: self.alignment,
position: self.position,
}
}
}

View File

@@ -1,4 +1,4 @@
- [v0.8.0](#v080)
- [v0.8.0](#v080)
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
- [Experimental ARM64 Support](#experimental-arm64-support)
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
@@ -7,6 +7,29 @@
- [Notable Bug Fixes](#notable-bug-fixes)
- [Command Line and API Changes](#command-line-and-api-changes)
- [Contributors](#contributors)
- [v0.9.0](#v090)
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
- [Block and Network Device Statistics](#block-and-network-device-statistics)
- [HTTP API Responses](#http-api-responses)
- [CPU Topology](#cpu-topology)
- [Release Build Optimization](#release-build-optimization)
- [Hypervisor Abstraction](#hypervisor-abstraction)
- [Snapshot/Restore Improvements](#snapshotrestore-improvements)
- [Virtio Memory Ballooning Support](#virtio-memory-ballooning-support)
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
- [Intel SGX Support](#intel-sgx-support)
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
- [Notable Bug Fixes](#notable-bug-fixes)
- [Contributors](#contributors)
- [v0.8.0](#v080)
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
- [Experimental ARM64 Support](#experimental-arm64-support)
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
- [Notable Bug Fixes](#notable-bug-fixes-1)
- [Command Line and API Changes](#command-line-and-api-changes)
- [Contributors](#contributors-1)
- [v0.7.0](#v070)
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
- [Alternative `libc` Support](#alternative-libc-support)
@@ -16,14 +39,14 @@
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
- [Updated Distribution Support](#updated-distribution-support)
- [Command Line and API Changes](#command-line-and-api-changes-1)
- [Contributors](#contributors-1)
- [Contributors](#contributors-2)
- [v0.6.0](#v060)
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
- [Command Line Interface](#command-line-interface)
- [PVH Boot](#pvh-boot)
- [Contributors](#contributors-2)
- [Contributors](#contributors-3)
- [v0.5.1](#v051)
- [v0.5.0](#v050)
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
@@ -31,7 +54,7 @@
- [New Interrupt Management Framework](#new-interrupt-management-framework)
- [Development Tools](#development-tools)
- [Kata Containers Integration](#kata-containers-integration)
- [Contributors](#contributors-3)
- [Contributors](#contributors-4)
- [v0.4.0](#v040)
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
@@ -40,7 +63,7 @@
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
- [PCI BAR reprogramming](#pci-bar-reprogramming)
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
- [Contributors](#contributors-4)
- [Contributors](#contributors-5)
- [v0.3.0](#v030)
- [Block device offloading](#block-device-offloading)
- [Network device backend](#network-device-backend)
@@ -67,6 +90,89 @@
- [Unit testing](#unit-testing)
- [Integration tests parallelization](#integration-tests-parallelization)
# v0.9.0
This release has been tracked through the [0.9.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/12).
Highlights for `cloud-hypervisor` version 0.9.0 include:
### `io_uring` Based Block Device Support
If the `io_uring` feature is enabled and the host kernel supports it then `io_uring` will be used for block devices. This results a very significant performance improvement.
### Block and Network Device Statistics
Statistics for activity of the `virtio` network and block devices is now exposed through a new `vm.counters` HTTP API entry point. These take the form of simple counters which can be used to observe the activity of the VM.
### HTTP API Responses
The HTTP API for adding devices now responds with the name that was assigned to the device as well the PCI BDF.
### CPU Topology
A `topology` parameter has been added to `--cpus` which allows the configuration of the guest CPU topology allowing the user to specify the numbers of sockets, packages per socket, cores per package and threads per core.
### Release Build Optimization
Our release build is now built with LTO (*Link Time Optimization*) which results in a ~20% reduction in the binary size.
### Hypervisor Abstraction
A new abstraction has been introduced, in the form of a `hypervisor` crate so as to enable the support of additional hypervisors beyond `KVM`.
### Snapshot/Restore Improvements
Multiple improvements have been made to the VM snapshot/restore support that was added in the last release. This includes persisting more vCPU state and in particular preserving the guest paravirtualized clock in order to avoid vCPU hangs inside the guest when running with multiple vCPUs.
### Virtio Memory Ballooning Support
A `virtio-balloon` device has been added, controlled through the `resize` control, which allows the reclamation of host memory by resizing a memory balloon inside the guest.
### Enhancements to ARM64 Support
The ARM64 support introduced in the last release has been further enhanced with support for using PCI for exposing devices into the guest as well as multiple bug fixes. It also now supports using an initramfs when booting.
### Intel SGX Support
The guest can now use Intel SGX if the host supports it. Details can be found in the dedicated [SGX documentation](docs/intel_sgx.md).
### `Seccomp` Sandbox Improvements
The most frequently used virtio devices are now isolated with their own `seccomp` filters. It is also now possible to pass `--seccomp=log` which result in the logging of requests that would have otherwise been denied to further aid development.
### Notable Bug Fixes
* Our `virtio-vsock` implementation has been resynced with the implementation from Firecracker and includes multiple bug fixes.
* CPU hotplug has been fixed so that it is now possible to add, remove, and re-add vCPUs (#1338)
* A workaround is now in place for when KVM reports MSRs available MSRs that are in fact unreadable preventing snapshot/restore from working correctly (#1543).
* `virtio-mmio` based devices are now more widely tested (#275).
* Multiple issues have been fixed with virtio device configuration (#1217)
* Console input was wrongly consumed by both `virtio-console` and the serial. (#1521)
### Contributors
Many thanks to everyone who has contributed to our 0.9.0 release including some new faces.
* Anatol Belski <ab@php.net>
* Bo Chen <chen.bo@intel.com>
* Dr. David Alan Gilbert <dgilbert@redhat.com>
* Henry Wang <Henry.Wang@arm.com>
* Howard Zhang <howard.zhang@arm.com>
* Hui Zhu <teawater@antfin.com>
* Jianyong Wu <jianyong.wu@arm.com>
* Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
* LiYa'nan <oliverliyn@gmail.com>
* Michael Zhao <michael.zhao@arm.com>
* Muminul Islam <muislam@microsoft.com>
* Praveen Paladugu <prapal@microsoft.com>
* Ricardo Koller <ricarkol@gmail.com>
* Rob Bradford <robert.bradford@intel.com>
* Samuel Ortiz <sameo@linux.intel.com>
* Sebastien Boeuf <sebastien.boeuf@intel.com>
* Stefano Garzarella <sgarzare@redhat.com>
* Wei Liu <liuwe@microsoft.com>
# v0.8.0
This release has been tracked through the [0.8.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/10).

View File

@@ -49,6 +49,22 @@ RUN if [ "$TARGETARCH" = "x86_64" ]; then \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*; fi
RUN if [ "$TARGETARCH" = "aarch64" ]; then \
# On AArch64, `setcap` binary should be installed via `libcap2-bin`.
# The `setcap` binary is used in integration tests.
apt-get update \
&& apt-get -yq upgrade \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq libcap2-bin \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
# Build and install the musl-gcc version of libfdt. This is to ensure
# the cloud-hypervisor binary can be built and linked using musl toolchain.
&& git clone https://github.com/dgibson/dtc.git && cd dtc \
&& sed -i 's/$(LIBFDT_archive) $(LIBFDT_lib)/$(LIBFDT_archive)/g' Makefile \
&& export CC=musl-gcc && make libfdt \
&& cp libfdt/libfdt.a /usr/lib/aarch64-linux-musl \
&& rm -rf /dtc; fi
# Fix the libssl-dev install
RUN cp /usr/include/"$TARGETARCH"-linux-gnu/openssl/opensslconf.h /usr/include/openssl/
ENV OPENSSL_DIR=/usr/lib/"$TARGETARCH"-linux-gnu/

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,16 @@
#
# Automatically generated file; DO NOT EDIT.
# Linux/x86 5.6.0-rc4 Kernel Configuration
#
#
# Compiler: gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0
# Linux/x86 5.8.0-rc4 Kernel Configuration
#
CONFIG_CC_VERSION_TEXT="gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0"
CONFIG_CC_IS_GCC=y
CONFIG_GCC_VERSION=80300
CONFIG_GCC_VERSION=90300
CONFIG_LD_VERSION=234000000
CONFIG_CLANG_VERSION=0
CONFIG_CC_CAN_LINK=y
CONFIG_CC_CAN_LINK_STATIC=y
CONFIG_CC_HAS_ASM_GOTO=y
CONFIG_CC_HAS_ASM_INLINE=y
CONFIG_CC_HAS_WARN_MAYBE_UNINITIALIZED=y
CONFIG_IRQ_WORK=y
CONFIG_BUILDTIME_TABLE_SORT=y
CONFIG_THREAD_INFO_IN_TASK=y
@@ -37,12 +35,14 @@ CONFIG_KERNEL_GZIP=y
# CONFIG_KERNEL_XZ is not set
# CONFIG_KERNEL_LZO is not set
# CONFIG_KERNEL_LZ4 is not set
CONFIG_DEFAULT_INIT=""
CONFIG_DEFAULT_HOSTNAME="cloud-hypervisor"
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
CONFIG_POSIX_MQUEUE=y
CONFIG_POSIX_MQUEUE_SYSCTL=y
# CONFIG_WATCH_QUEUE is not set
CONFIG_CROSS_MEMORY_ATTACH=y
# CONFIG_USELIB is not set
CONFIG_AUDIT=y
@@ -57,6 +57,7 @@ CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
CONFIG_GENERIC_PENDING_IRQ=y
CONFIG_GENERIC_IRQ_MIGRATION=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_GENERIC_MSI_IRQ=y
@@ -70,7 +71,6 @@ CONFIG_SPARSE_IRQ=y
# end of IRQ subsystem
CONFIG_CLOCKSOURCE_WATCHDOG=y
CONFIG_ARCH_CLOCKSOURCE_DATA=y
CONFIG_ARCH_CLOCKSOURCE_INIT=y
CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE=y
CONFIG_GENERIC_TIME_VSYSCALL=y
@@ -102,6 +102,7 @@ CONFIG_TICK_CPU_ACCOUNTING=y
# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
# CONFIG_IRQ_TIME_ACCOUNTING is not set
CONFIG_HAVE_SCHED_AVG_IRQ=y
# CONFIG_SCHED_THERMAL_PRESSURE is not set
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_BSD_PROCESS_ACCT_V3=y
CONFIG_TASKSTATS=y
@@ -135,6 +136,7 @@ CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y
#
# Scheduler features
#
# CONFIG_UCLAMP_TASK is not set
# end of Scheduler features
CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
@@ -147,7 +149,6 @@ CONFIG_CGROUPS=y
CONFIG_PAGE_COUNTER=y
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
CONFIG_MEMCG_SWAP_ENABLED=y
CONFIG_MEMCG_KMEM=y
CONFIG_BLK_CGROUP=y
CONFIG_CGROUP_WRITEBACK=y
@@ -215,6 +216,7 @@ CONFIG_SHMEM=y
CONFIG_AIO=y
CONFIG_IO_URING=y
CONFIG_ADVISE_SYSCALLS=y
CONFIG_HAVE_ARCH_USERFAULTFD_WP=y
CONFIG_MEMBARRIER=y
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
@@ -257,7 +259,6 @@ CONFIG_X86_64=y
CONFIG_X86=y
CONFIG_INSTRUCTION_DECODER=y
CONFIG_OUTPUT_FORMAT="elf64-x86-64"
CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig"
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_MMU=y
@@ -313,7 +314,6 @@ CONFIG_PARAVIRT_SPINLOCKS=y
CONFIG_KVM_GUEST=y
CONFIG_ARCH_CPUIDLE_HALTPOLL=y
CONFIG_PVH=y
CONFIG_KVM_DEBUG_FS=y
CONFIG_PARAVIRT_TIME_ACCOUNTING=y
CONFIG_PARAVIRT_CLOCK=y
# CONFIG_JAILHOUSE_GUEST is not set
@@ -379,7 +379,6 @@ CONFIG_AMD_MEM_ENCRYPT=y
CONFIG_NUMA=y
# CONFIG_AMD_NUMA is not set
CONFIG_X86_64_ACPI_NUMA=y
CONFIG_NODES_SPAN_OTHER_NODES=y
# CONFIG_NUMA_EMU is not set
CONFIG_NODES_SHIFT=10
CONFIG_ARCH_SPARSEMEM_ENABLE=y
@@ -454,6 +453,7 @@ CONFIG_ARCH_HIBERNATION_HEADER=y
# CONFIG_SUSPEND is not set
CONFIG_HIBERNATE_CALLBACKS=y
CONFIG_HIBERNATION=y
# CONFIG_HIBERNATION_SNAPSHOT_DEV is not set
CONFIG_PM_STD_PARTITION=""
CONFIG_PM_SLEEP=y
CONFIG_PM_SLEEP_SMP=y
@@ -479,6 +479,7 @@ CONFIG_ACPI_REV_OVERRIDE_POSSIBLE=y
# CONFIG_ACPI_AC is not set
# CONFIG_ACPI_BATTERY is not set
# CONFIG_ACPI_BUTTON is not set
# CONFIG_ACPI_TINY_POWER_BUTTON is not set
# CONFIG_ACPI_VIDEO is not set
# CONFIG_ACPI_FAN is not set
# CONFIG_ACPI_TAD is not set
@@ -519,6 +520,7 @@ CONFIG_X86_PM_TIMER=y
# CPU Frequency scaling
#
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_ATTR_SET=y
# CONFIG_CPU_FREQ_STAT is not set
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
@@ -531,7 +533,7 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
# CONFIG_CPU_FREQ_GOV_SCHEDUTIL is not set
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
#
# CPU frequency scaling drivers
@@ -600,6 +602,7 @@ CONFIG_EFI_ESRT=y
CONFIG_EFI_RUNTIME_MAP=y
# CONFIG_EFI_FAKE_MEMMAP is not set
CONFIG_EFI_RUNTIME_WRAPPERS=y
CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y
# CONFIG_EFI_CAPSULE_LOADER is not set
# CONFIG_EFI_TEST is not set
# CONFIG_APPLE_PROPERTIES is not set
@@ -634,9 +637,10 @@ CONFIG_KVM=y
# CONFIG_KVM_WERROR is not set
CONFIG_KVM_INTEL=y
# CONFIG_KVM_AMD is not set
# CONFIG_VHOST_NET is not set
# CONFIG_VHOST_VSOCK is not set
# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set
CONFIG_AS_AVX512=y
CONFIG_AS_SHA1_NI=y
CONFIG_AS_SHA256_NI=y
CONFIG_AS_TPAUSE=y
#
# General architecture-dependent options
@@ -670,7 +674,6 @@ CONFIG_HAVE_ASM_MODVERSIONS=y
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
CONFIG_HAVE_RSEQ=y
CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y
CONFIG_HAVE_CLK=y
CONFIG_HAVE_HW_BREAKPOINT=y
CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y
CONFIG_HAVE_USER_RETURN_NOTIFIER=y
@@ -705,7 +708,6 @@ CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y
CONFIG_HAVE_ARCH_SOFT_DIRTY=y
CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
CONFIG_MODULES_USE_ELF_RELA=y
CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y
CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
CONFIG_HAVE_ARCH_MMAP_RND_BITS=y
CONFIG_HAVE_EXIT_THREAD=y
@@ -731,7 +733,6 @@ CONFIG_ARCH_HAS_MEM_ENCRYPT=y
CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
# end of GCOV-based kernel profiling
CONFIG_PLUGIN_HOSTCC=""
CONFIG_HAVE_GCC_PLUGINS=y
# end of General architecture-dependent options
@@ -755,6 +756,7 @@ CONFIG_BLK_WBT=y
CONFIG_BLK_WBT_MQ=y
CONFIG_BLK_DEBUG_FS=y
# CONFIG_BLK_SED_OPAL is not set
# CONFIG_BLK_INLINE_ENCRYPTION is not set
#
# Partition Types
@@ -803,6 +805,7 @@ CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y
CONFIG_ARCH_HAS_SYNC_CORE_BEFORE_USERMODE=y
CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y
CONFIG_FREEZER=y
@@ -829,8 +832,8 @@ CONFIG_HAVE_MEMORY_PRESENT=y
CONFIG_SPARSEMEM_EXTREME=y
CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
CONFIG_SPARSEMEM_VMEMMAP=y
CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
CONFIG_HAVE_FAST_GUP=y
CONFIG_NUMA_KEEP_MEMINFO=y
CONFIG_MEMORY_ISOLATION=y
CONFIG_HAVE_BOOTMEM_INFO_NODE=y
CONFIG_MEMORY_HOTPLUG=y
@@ -841,6 +844,7 @@ CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_MEMORY_BALLOON=y
CONFIG_BALLOON_COMPACTION=y
CONFIG_COMPACTION=y
CONFIG_PAGE_REPORTING=y
CONFIG_MIGRATION=y
CONFIG_CONTIG_ALLOC=y
CONFIG_PHYS_ADDR_T_64BIT=y
@@ -854,16 +858,27 @@ CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
CONFIG_ARCH_WANTS_THP_SWAP=y
CONFIG_THP_SWAP=y
CONFIG_TRANSPARENT_HUGE_PAGECACHE=y
CONFIG_CLEANCACHE=y
CONFIG_FRONTSWAP=y
# CONFIG_CMA is not set
CONFIG_ZSWAP=y
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_DEFLATE is not set
CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZO=y
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_842 is not set
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4 is not set
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4HC is not set
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_ZSTD is not set
CONFIG_ZSWAP_COMPRESSOR_DEFAULT="lzo"
CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_Z3FOLD is not set
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_ZSWAP_ZPOOL_DEFAULT="zbud"
# CONFIG_ZSWAP_DEFAULT_ON is not set
CONFIG_ZPOOL=y
CONFIG_ZBUD=y
# CONFIG_Z3FOLD is not set
CONFIG_ZSMALLOC=y
# CONFIG_PGTABLE_MAPPING is not set
# CONFIG_ZSMALLOC_PGTABLE_MAPPING is not set
CONFIG_ZSMALLOC_STAT=y
CONFIG_GENERIC_EARLY_IOREMAP=y
# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
@@ -965,6 +980,7 @@ CONFIG_VIRTIO_VSOCKETS_COMMON=y
# CONFIG_HSR is not set
# CONFIG_NET_SWITCHDEV is not set
# CONFIG_NET_L3_MASTER_DEV is not set
# CONFIG_QRTR is not set
# CONFIG_NET_NCSI is not set
CONFIG_RPS=y
CONFIG_RFS_ACCEL=y
@@ -1056,6 +1072,11 @@ CONFIG_HOTPLUG_PCI_ACPI=y
# CONFIG_PCI_MESON is not set
# end of DesignWare PCI Core Support
#
# Mobiveil PCIe Core Support
#
# end of Mobiveil PCIe Core Support
#
# Cadence PCIe controllers support
#
@@ -1110,6 +1131,7 @@ CONFIG_GENERIC_CPU_VULNERABILITIES=y
#
# Bus devices
#
# CONFIG_MHI_BUS is not set
# end of Bus devices
CONFIG_CONNECTOR=y
@@ -1207,6 +1229,7 @@ CONFIG_VIRTIO_BLK=y
# CONFIG_MISC_ALCOR_PCI is not set
# CONFIG_MISC_RTSX_PCI is not set
# CONFIG_HABANA_AI is not set
# CONFIG_UACCE is not set
# end of Misc devices
CONFIG_HAVE_IDE=y
@@ -1244,6 +1267,7 @@ CONFIG_NET_CORE=y
# CONFIG_IPVLAN is not set
# CONFIG_VXLAN is not set
# CONFIG_GENEVE is not set
# CONFIG_BAREUDP is not set
# CONFIG_GTP is not set
# CONFIG_MACSEC is not set
# CONFIG_NETCONSOLE is not set
@@ -1323,7 +1347,6 @@ CONFIG_INPUT_KEYBOARD=y
CONFIG_INPUT_MISC=y
# CONFIG_INPUT_AD714X is not set
# CONFIG_INPUT_E3X0_BUTTON is not set
# CONFIG_INPUT_MSM_VIBRATOR is not set
# CONFIG_INPUT_PCSPKR is not set
# CONFIG_INPUT_ATLAS_BTNS is not set
CONFIG_INPUT_UINPUT=y
@@ -1352,14 +1375,7 @@ CONFIG_HW_CONSOLE=y
CONFIG_VT_HW_CONSOLE_BINDING=y
CONFIG_UNIX98_PTYS=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_SERIAL_NONSTANDARD is not set
# CONFIG_NOZOMI is not set
# CONFIG_N_GSM is not set
# CONFIG_TRACE_SINK is not set
# CONFIG_NULL_TTY is not set
# CONFIG_LDISC_AUTOLOAD is not set
CONFIG_DEVMEM=y
# CONFIG_DEVKMEM is not set
#
# Serial drivers
@@ -1390,6 +1406,7 @@ CONFIG_SERIAL_8250_MID=y
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_SERIAL_JSM is not set
# CONFIG_SERIAL_LANTIQ is not set
# CONFIG_SERIAL_SCCNXP is not set
# CONFIG_SERIAL_ALTERA_JTAGUART is not set
# CONFIG_SERIAL_ALTERA_UART is not set
@@ -1399,12 +1416,18 @@ CONFIG_SERIAL_ARC_NR_PORTS=1
# CONFIG_SERIAL_RP2 is not set
# CONFIG_SERIAL_FSL_LPUART is not set
# CONFIG_SERIAL_FSL_LINFLEXUART is not set
# CONFIG_SERIAL_SPRD is not set
# end of Serial drivers
# CONFIG_SERIAL_NONSTANDARD is not set
# CONFIG_N_GSM is not set
# CONFIG_NOZOMI is not set
# CONFIG_NULL_TTY is not set
# CONFIG_TRACE_SINK is not set
CONFIG_HVC_DRIVER=y
CONFIG_SERIAL_DEV_BUS=y
CONFIG_SERIAL_DEV_CTRL_TTYPORT=y
# CONFIG_TTY_PRINTK is not set
CONFIG_HVC_DRIVER=y
CONFIG_VIRTIO_CONSOLE=y
# CONFIG_IPMI_HANDLER is not set
CONFIG_HW_RANDOM=y
@@ -1413,16 +1436,18 @@ CONFIG_HW_RANDOM_INTEL=y
CONFIG_HW_RANDOM_AMD=y
CONFIG_HW_RANDOM_VIA=y
CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_NVRAM=y
# CONFIG_APPLICOM is not set
# CONFIG_MWAVE is not set
CONFIG_DEVMEM=y
# CONFIG_DEVKMEM is not set
CONFIG_NVRAM=y
CONFIG_RAW_DRIVER=y
CONFIG_MAX_RAW_DEVS=256
CONFIG_DEVPORT=y
# CONFIG_HPET is not set
CONFIG_HANGCHECK_TIMER=y
# CONFIG_TCG_TPM is not set
CONFIG_TELCLOCK=y
CONFIG_DEVPORT=y
# CONFIG_XILLYBUS is not set
# end of Character devices
@@ -1462,6 +1487,7 @@ CONFIG_PTP_1588_CLOCK=y
# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
#
CONFIG_PTP_1588_CLOCK_KVM=y
# CONFIG_PTP_1588_CLOCK_VMW is not set
# end of PTP clock support
# CONFIG_PINCTRL is not set
@@ -1530,6 +1556,7 @@ CONFIG_BCMA_POSSIBLE=y
# CONFIG_REGULATOR is not set
# CONFIG_RC_CORE is not set
# CONFIG_MEDIA_CEC_SUPPORT is not set
# CONFIG_MEDIA_SUPPORT is not set
#
@@ -1546,11 +1573,6 @@ CONFIG_VGA_ARB_MAX_GPUS=16
#
# end of ARM devices
#
# ACP (Audio CoProcessor) Configuration
#
# end of ACP (Audio CoProcessor) Configuration
#
# Frame buffer Devices
#
@@ -1667,6 +1689,7 @@ CONFIG_HID_GENERIC=y
# CONFIG_HID_EZKEY is not set
# CONFIG_HID_GEMBIRD is not set
# CONFIG_HID_GFRM is not set
# CONFIG_HID_GLORIOUS is not set
# CONFIG_HID_KEYTOUCH is not set
# CONFIG_HID_KYE is not set
# CONFIG_HID_WALTOP is not set
@@ -1767,6 +1790,7 @@ CONFIG_HSU_DMA=y
# DMABUF options
#
# CONFIG_SYNC_FILE is not set
# CONFIG_DMABUF_MOVE_NOTIFY is not set
# CONFIG_DMABUF_HEAPS is not set
# end of DMABUF options
@@ -1803,6 +1827,8 @@ CONFIG_VIRTIO_MEM=y
CONFIG_VIRTIO_INPUT=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
# CONFIG_VDPA is not set
# CONFIG_VHOST_MENU is not set
#
# Microsoft Hyper-V guest support
@@ -1817,15 +1843,10 @@ CONFIG_PMC_ATOM=y
# CONFIG_MFD_CROS_EC is not set
# CONFIG_CHROME_PLATFORMS is not set
# CONFIG_MELLANOX_PLATFORM is not set
CONFIG_HAVE_CLK=y
CONFIG_CLKDEV_LOOKUP=y
CONFIG_HAVE_CLK_PREPARE=y
CONFIG_COMMON_CLK=y
#
# Common Clock Framework
#
# end of Common Clock Framework
# CONFIG_HWSPINLOCK is not set
#
@@ -1855,7 +1876,9 @@ CONFIG_IOMMU_DMA=y
# CONFIG_INTEL_IOMMU is not set
# CONFIG_IRQ_REMAP is not set
CONFIG_VIRTIO_IOMMU=y
CONFIG_VIRTIO_IOMMU_TOPOLOGY_HELPERS=y
CONFIG_VIRTIO_IOMMU_TOPOLOGY=y
CONFIG_ACPI_VIOT=y
#
# Remoteproc drivers
@@ -1986,6 +2009,7 @@ CONFIG_DAX=y
# CONFIG_SLIMBUS is not set
# CONFIG_INTERCONNECT is not set
# CONFIG_COUNTER is not set
# CONFIG_MOST is not set
# end of Device Drivers
#
@@ -2060,7 +2084,7 @@ CONFIG_CACHEFILES=y
# end of CD-ROM/DVD Filesystems
#
# DOS/FAT/NT Filesystems
# DOS/FAT/EXFAT/NT Filesystems
#
CONFIG_FAT_FS=y
CONFIG_MSDOS_FS=y
@@ -2068,8 +2092,9 @@ CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
# CONFIG_FAT_DEFAULT_UTF8 is not set
# CONFIG_EXFAT_FS is not set
# CONFIG_NTFS_FS is not set
# end of DOS/FAT/NT Filesystems
# end of DOS/FAT/EXFAT/NT Filesystems
#
# Pseudo filesystems
@@ -2157,7 +2182,6 @@ CONFIG_IO_WQ=y
CONFIG_KEYS=y
# CONFIG_KEYS_REQUEST_CACHE is not set
CONFIG_PERSISTENT_KEYRINGS=y
# CONFIG_BIG_KEYS is not set
# CONFIG_ENCRYPTED_KEYS is not set
# CONFIG_KEY_DH_OPERATIONS is not set
# CONFIG_SECURITY_DMESG_RESTRICT is not set
@@ -2168,6 +2192,7 @@ CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y
# CONFIG_HARDENED_USERCOPY is not set
CONFIG_FORTIFY_SOURCE=y
# CONFIG_STATIC_USERMODEHELPER is not set
# CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set
CONFIG_DEFAULT_SECURITY_DAC=y
CONFIG_LSM="yama,loadpin,safesetid,integrity"
@@ -2400,11 +2425,13 @@ CONFIG_GENERIC_STRNLEN_USER=y
CONFIG_GENERIC_NET_UTILS=y
CONFIG_GENERIC_FIND_FIRST_BIT=y
# CONFIG_CORDIC is not set
# CONFIG_PRIME_NUMBERS is not set
CONFIG_RATIONAL=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_IOMAP=y
CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
CONFIG_ARCH_HAS_FAST_MULTIPLIER=y
CONFIG_ARCH_USE_SYM_ANNOTATIONS=y
CONFIG_CRC_CCITT=y
CONFIG_CRC16=y
CONFIG_CRC_T10DIF=y
@@ -2442,6 +2469,7 @@ CONFIG_DECOMPRESS_LZMA=y
CONFIG_DECOMPRESS_XZ=y
CONFIG_DECOMPRESS_LZO=y
CONFIG_DECOMPRESS_LZ4=y
CONFIG_GENERIC_ALLOCATOR=y
CONFIG_INTERVAL_TREE=y
CONFIG_XARRAY_MULTI=y
CONFIG_ASSOCIATIVE_ARRAY=y
@@ -2453,6 +2481,7 @@ CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED=y
CONFIG_SWIOTLB=y
CONFIG_DMA_COHERENT_POOL=y
# CONFIG_DMA_API_DEBUG is not set
CONFIG_SGL_ALLOC=y
CONFIG_CPU_RMAP=y
@@ -2489,6 +2518,7 @@ CONFIG_CONSOLE_LOGLEVEL_QUIET=4
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
# CONFIG_BOOT_PRINTK_DELAY is not set
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DYNAMIC_DEBUG_CORE=y
CONFIG_SYMBOLIC_ERRNAME=y
# CONFIG_DEBUG_BUGVERBOSE is not set
# end of printk and dmesg options
@@ -2502,7 +2532,6 @@ CONFIG_FRAME_WARN=2048
CONFIG_STRIP_ASM_SYMS=y
# CONFIG_READABLE_ASM is not set
# CONFIG_HEADERS_INSTALL is not set
CONFIG_OPTIMIZE_INLINING=y
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_SECTION_MISMATCH_WARN_ONLY=y
CONFIG_FRAME_POINTER=y
@@ -2516,12 +2545,12 @@ CONFIG_STACK_VALIDATION=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
CONFIG_MAGIC_SYSRQ_SERIAL=y
CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
CONFIG_DEBUG_FS=y
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_KGDB is not set
CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y
# CONFIG_UBSAN is not set
CONFIG_UBSAN_ALIGNMENT=y
# end of Generic Kernel Debugging Instruments
CONFIG_DEBUG_KERNEL=y
@@ -2535,6 +2564,8 @@ CONFIG_DEBUG_MISC=y
# CONFIG_PAGE_OWNER is not set
# CONFIG_PAGE_POISONING is not set
# CONFIG_DEBUG_RODATA_TEST is not set
CONFIG_ARCH_HAS_DEBUG_WX=y
# CONFIG_DEBUG_WX is not set
CONFIG_GENERIC_PTDUMP=y
# CONFIG_PTDUMP_DEBUGFS is not set
# CONFIG_DEBUG_OBJECTS is not set
@@ -2544,7 +2575,9 @@ CONFIG_HAVE_DEBUG_KMEMLEAK=y
# CONFIG_DEBUG_KMEMLEAK is not set
# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_SCHED_STACK_END_CHECK is not set
CONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y
# CONFIG_DEBUG_VM is not set
# CONFIG_DEBUG_VM_PGTABLE is not set
CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y
# CONFIG_DEBUG_VIRTUAL is not set
CONFIG_DEBUG_MEMORY_INIT=y
@@ -2552,6 +2585,7 @@ CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_HAVE_ARCH_KASAN=y
CONFIG_HAVE_ARCH_KASAN_VMALLOC=y
CONFIG_CC_HAS_KASAN_GENERIC=y
CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y
# CONFIG_KASAN is not set
CONFIG_KASAN_STACK=1
# end of Memory Debugging
@@ -2569,6 +2603,7 @@ CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y
# CONFIG_HARDLOCKUP_DETECTOR is not set
# CONFIG_DETECT_HUNG_TASK is not set
# CONFIG_WQ_WATCHDOG is not set
# CONFIG_TEST_LOCKUP is not set
# end of Debug Oops, Lockups and Hangs
#
@@ -2643,6 +2678,7 @@ CONFIG_TRACING_SUPPORT=y
# CONFIG_FTRACE is not set
# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KCSAN=y
CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y
CONFIG_STRICT_DEVMEM=y
# CONFIG_IO_STRICT_DEVMEM is not set
@@ -2656,8 +2692,6 @@ CONFIG_EARLY_PRINTK=y
# CONFIG_EARLY_PRINTK_DBGP is not set
# CONFIG_EARLY_PRINTK_USB_XDBC is not set
# CONFIG_EFI_PGT_DUMP is not set
# CONFIG_DEBUG_WX is not set
CONFIG_DOUBLEFAULT=y
# CONFIG_DEBUG_TLBFLUSH is not set
CONFIG_HAVE_MMIOTRACE_SUPPORT=y
# CONFIG_X86_DECODER_SELFTEST is not set
@@ -2688,6 +2722,7 @@ CONFIG_CC_HAS_SANCOV_TRACE_PC=y
CONFIG_RUNTIME_TESTING_MENU=y
# CONFIG_LKDTM is not set
# CONFIG_TEST_LIST_SORT is not set
# CONFIG_TEST_MIN_HEAP is not set
# CONFIG_TEST_SORT is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
# CONFIG_RBTREE_TEST is not set

96
rpm/cloud-hypervisor.spec Normal file
View File

@@ -0,0 +1,96 @@
Name: cloud-hypervisor
Summary: Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM.
Version: 0.8.0
Release: 0%{?dist}
License: ASL 2.0 or BSD-3-clause
Group: Applications/System
Source0: https://github.com/cloud-hypervisor/cloud-hypervisor/archive/v%{version}.tar.gz
ExclusiveArch: x86_64
BuildRequires: gcc
BuildRequires: glibc-devel
BuildRequires: binutils
BuildRequires: git
%if 0%{?fedora} || 0%{?rhel}
BuildRequires: rust
BuildRequires: cargo
%endif
Requires: bash
Requires: glibc
Requires: libgcc
Requires: libcap
%description
Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM. The project focuses on exclusively running modern, cloud workloads, on top of a limited set of hardware architectures and platforms. Cloud workloads refers to those that are usually run by customers inside a cloud provider. For our purposes this means modern Linux* distributions with most I/O handled by paravirtualised devices (i.e. virtio), no requirement for legacy devices and recent CPUs and KVM.
%prep
%setup -q
%install
rm -rf %{buildroot}
install -d %{buildroot}%{_bindir}
install -D -m755 ./target/x86_64-unknown-linux-gnu/release/cloud-hypervisor %{buildroot}%{_bindir}
install -D -m755 ./target/x86_64-unknown-linux-gnu/release/ch-remote %{buildroot}%{_bindir}
install -d %{buildroot}%{_libdir}
install -d %{buildroot}%{_libdir}/cloud-hypervisor
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_blk %{buildroot}%{_libdir}/cloud-hypervisor
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_fs %{buildroot}%{_libdir}/cloud-hypervisor
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor
install -d %{buildroot}%{_libdir}/cloud-hypervisor/static
install -D -m755 target/x86_64-unknown-linux-musl/release/cloud-hypervisor %{buildroot}%{_libdir}/cloud-hypervisor/static
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_blk %{buildroot}%{_libdir}/cloud-hypervisor/static
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_fs %{buildroot}%{_libdir}/cloud-hypervisor/static
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor/static
install -D -m755 target/x86_64-unknown-linux-musl/release/ch-remote %{buildroot}%{_libdir}/cloud-hypervisor/static
%build
cargo_version=$(cargo --version)
if [[ $? -ne 0 ]]; then
echo "Cargo not found, please install cargo. exiting"
exit 0
fi
which rustup
if [[ $? -ne 0 ]]; then
echo "Rustup not found please install rustup #curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
fi
echo ${cargo_version}
rustup target list --installed | grep x86_64-unknown-linux-gnu
if [[ $? -ne 0 ]]; then
echo "Target x86_64-unknown-linux-gnu not found, please install(#rustup target add x86_64-unknown-linux-gnu). exiting"
fi
rustup target list --installed | grep x86_64-unknown-linux-musl
if [[ $? -ne 0 ]]; then
echo "Target x86_64-unknown-linux-musl not found, please install(#rustup target add x86_64-unknown-linux-musl). exiting"
fi
cargo build --release --target=x86_64-unknown-linux-gnu --all
cargo build --release --target=x86_64-unknown-linux-musl --all
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%{_bindir}/*
%{_libdir}/cloud-hypervisor
%license LICENSE-APACHE
%license LICENSE-BSD-3-Clause
%post
setcap cap_net_admin+ep %{_bindir}/cloud-hypervisor
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/vhost_user_net
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/static/cloud-hypervisor
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/static/vhost_user_net
setcap cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_setgid,cap_setuid,cap_mknod,cap_setfcap,cap_sys_admin+epi %{_libdir}/cloud-hypervisor/vhost_user_fs
setcap cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_setgid,cap_setuid,cap_mknod,cap_setfcap,cap_sys_admin+epi %{_libdir}/cloud-hypervisor/static/vhost_user_fs
%changelog
* Wed Jul 22 2020 Muminul Islam <muislam@microsoft.com> 0.8.0-0
- Initial version

View File

@@ -1,10 +1,6 @@
#!/bin/bash
set -x
rm /tmp/clear-cloudinit.img
mkdosfs -n config-2 -C /tmp/clear-cloudinit.img 8192
mcopy -oi /tmp/clear-cloudinit.img -s test_data/cloud-init/clear/openstack ::
rm /tmp/ubuntu-cloudinit.img
mkdosfs -n cidata -C /tmp/ubuntu-cloudinit.img 8192
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/user-data ::

View File

@@ -7,7 +7,7 @@
CLI_NAME="Cloud Hypervisor"
CTR_IMAGE_TAG="cloudhypervisor/dev"
CTR_IMAGE_VERSION="v3"
CTR_IMAGE_VERSION="v4"
CTR_IMAGE="${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}"
DOCKER_RUNTIME="docker"
@@ -27,6 +27,10 @@ CTR_CLH_CARGO_BUILT_DIR="${CTR_CLH_ROOT_DIR}/build"
CTR_CLH_CARGO_TARGET="${CTR_CLH_CARGO_BUILT_DIR}/cargo_target"
CTR_CLH_INTEGRATION_WORKLOADS="/root/workloads"
# Container networking option
CTR_CLH_NET="host"
[ $(uname -m) = "aarch64" ] && CTR_CLH_NET="bridge"
# Cargo paths
# Full path to the cargo registry dir on the host. This appears on the host
# because we want to persist the cargo registry across container invocations.
@@ -167,6 +171,9 @@ cmd_help() {
echo " clean [<cargo args>]]"
echo " Remove the Cloud Hypervisor artifacts."
echo ""
echo " shell"
echo " Run the development container into an interactive, privileged BASH shell."
echo ""
echo " help"
echo " Display this help message."
echo ""
@@ -201,16 +208,11 @@ cmd_build() {
[ $build = "release" ] && cargo_args+=("--release")
cargo_args+=(--target "$target")
[ $(uname -m) = "aarch64" ] && cargo_args+=("--no-default-features")
[ $(uname -m) = "aarch64" ] && cargo_args+=(--features "mmio")
[ $(uname -m) = "aarch64" ] && cargo_args+=(--features "mmio,kvm")
rustflags=""
if [ $(uname -m) = "aarch64" ] && [ $libc = "musl" ] ; then
rustflags="-C link-arg=-lgcc"
fi
# A workaround on Arm64 to avoid build errors in kvm-bindings
if [ $(uname -m) = "aarch64" ]; then
sed -i 's/"with-serde",\ //g' "$CLH_ROOT_DIR"/vmm/Cargo.toml
rustflags="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
fi
$DOCKER_RUNTIME run \
@@ -221,7 +223,7 @@ cmd_build() {
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" \
--env RUSTFLAGS="$rustflags" \
"$CTR_IMAGE" \
cargo build \
cargo build --all \
--target-dir "$CTR_CLH_CARGO_TARGET" \
"${cargo_args[@]}" && say "Binaries placed under $CLH_CARGO_TARGET/$target/$build"
}
@@ -309,7 +311,7 @@ cmd_tests() {
--privileged \
--security-opt seccomp=unconfined \
--ipc=host \
--net=host \
--net="$CTR_CLH_NET" \
--mount type=tmpfs,destination=/tmp \
--volume /dev:/dev \
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" \
@@ -317,7 +319,7 @@ cmd_tests() {
--env USER="root" \
--env CH_LIBC="${libc}" \
"$CTR_IMAGE" \
./scripts/run_integration_tests.sh "$@" || fix_dir_perms $? || exit $?
./scripts/run_integration_tests_$(uname -m).sh "$@" || fix_dir_perms $? || exit $?
fi
fix_dir_perms $?
@@ -351,6 +353,28 @@ cmd_build-container() {
$BUILD_DIR
}
cmd_shell() {
say_warn "Starting a privileged shell prompt as root ..."
say_warn "WARNING: Your $CLH_ROOT_DIR folder will be bind-mounted in the container under $CTR_CLH_ROOT_DIR"
$DOCKER_RUNTIME run \
-ti \
--workdir "$CTR_CLH_ROOT_DIR" \
--rm \
--privileged \
--security-opt seccomp=unconfined \
--ipc=host \
--net="$CTR_CLH_NET" \
--tmpfs /tmp:exec \
--volume /dev:/dev \
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" \
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
--env USER="root" \
--entrypoint bash \
"$CTR_IMAGE"
fix_dir_perms $?
}
# Parse main command line args.
#
while [ $# -gt 0 ]; do

View File

@@ -12,17 +12,14 @@ time which cargo-audit || cargo install cargo-audit
# Run cargo builds and checks
time cargo clippy --all-targets --all-features -- -D warnings
time cargo rustc --bin cloud-hypervisor -- -D warnings
time cargo rustc --bin vhost_user_net -- -D warnings
time cargo rustc -p vhost_user_net --bin vhost_user_net -- -D warnings
time cargo test
time cargo audit
time cargo clippy --all-targets --no-default-features --features "pci,acpi" -- -D warnings
time cargo rustc --bin cloud-hypervisor --no-default-features --features "pci,acpi" -- -D warnings
time cargo rustc --bin vhost_user_net --no-default-features --features "pci,acpi" -- -D warnings
time cargo clippy --all-targets --no-default-features --features "pci" -- -D warnings
time cargo rustc --bin cloud-hypervisor --no-default-features --features "pci" -- -D warnings
time cargo rustc --bin vhost_user_net --no-default-features --features "pci" -- -D warnings
time cargo clippy --all-targets --no-default-features --features "mmio" -- -D warnings
time cargo rustc --bin cloud-hypervisor --no-default-features --features "mmio" -- -D warnings
time cargo rustc --bin vhost_user_net --no-default-features --features "mmio" -- -D warnings
time cargo clippy --all-targets --no-default-features --features "pci,acpi,kvm" -- -D warnings
time cargo rustc --bin cloud-hypervisor --no-default-features --features "pci,acpi,kvm" -- -D warnings
time cargo clippy --all-targets --no-default-features --features "pci,kvm" -- -D warnings
time cargo rustc --bin cloud-hypervisor --no-default-features --features "pci,kvm" -- -D warnings
time cargo clippy --all-targets --no-default-features --features "mmio,kvm" -- -D warnings
time cargo rustc --bin cloud-hypervisor --no-default-features --features "mmio,kvm" -- -D warnings
time cargo fmt -- --check
time cargo build --release
time cargo build --all --release

View File

@@ -0,0 +1,239 @@
#!/bin/bash
set -x
source $HOME/.cargo/env
export BUILD_TARGET=${BUILD_TARGET-aarch64-unknown-linux-gnu}
WORKLOADS_DIR="$HOME/workloads"
WORKLOADS_LOCK="$WORKLOADS_DIR/integration_test.lock"
mkdir -p "$WORKLOADS_DIR"
update_workloads() {
cp scripts/sha1sums-aarch64 $WORKLOADS_DIR
BIONIC_OS_IMAGE_DOWNLOAD_NAME="bionic-server-cloudimg-arm64.img"
BIONIC_OS_IMAGE_DOWNLOAD_URL="https://cloudhypervisorstorage.blob.core.windows.net/images/$BIONIC_OS_IMAGE_DOWNLOAD_NAME"
BIONIC_OS_DOWNLOAD_IMAGE="$WORKLOADS_DIR/$BIONIC_OS_IMAGE_DOWNLOAD_NAME"
if [ ! -f "$BIONIC_OS_DOWNLOAD_IMAGE" ]; then
pushd $WORKLOADS_DIR
time wget --quiet $BIONIC_OS_IMAGE_DOWNLOAD_URL || exit 1
popd
fi
BIONIC_OS_RAW_IMAGE_NAME="bionic-server-cloudimg-arm64.raw"
BIONIC_OS_RAW_IMAGE="$WORKLOADS_DIR/$BIONIC_OS_RAW_IMAGE_NAME"
if [ ! -f "$BIONIC_OS_RAW_IMAGE" ]; then
pushd $WORKLOADS_DIR
time qemu-img convert -p -f qcow2 -O raw $BIONIC_OS_IMAGE_DOWNLOAD_NAME $BIONIC_OS_RAW_IMAGE_NAME || exit 1
popd
fi
# Convert the raw image to qcow2 image to remove compressed blocks from the disk. Therefore letting the
# qcow2 format image can be directly used in the integration test.
BIONIC_OS_QCOW2_IMAGE_UNCOMPRESSED_NAME="bionic-server-cloudimg-arm64.qcow2"
BIONIC_OS_QCOW2_UNCOMPRESSED_IMAGE="$WORKLOADS_DIR/$BIONIC_OS_QCOW2_IMAGE_UNCOMPRESSED_NAME"
if [ ! -f "$BIONIC_OS_QCOW2_UNCOMPRESSED_IMAGE" ]; then
pushd $WORKLOADS_DIR
time qemu-img convert -p -f raw -O qcow2 $BIONIC_OS_RAW_IMAGE_NAME $BIONIC_OS_QCOW2_UNCOMPRESSED_IMAGE || exit 1
popd
fi
FOCAL_OS_RAW_IMAGE_NAME="focal-server-cloudimg-arm64-custom.raw"
FOCAL_OS_RAW_IMAGE_DOWNLOAD_URL="https://cloudhypervisorstorage.blob.core.windows.net/images/$FOCAL_OS_RAW_IMAGE_NAME"
FOCAL_OS_RAW_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_RAW_IMAGE_NAME"
if [ ! -f "$FOCAL_OS_RAW_IMAGE" ]; then
pushd $WORKLOADS_DIR
time wget --quiet $FOCAL_OS_RAW_IMAGE_DOWNLOAD_URL || exit 1
popd
fi
# Convert the raw image to qcow2 image to remove compressed blocks from the disk. Therefore letting the
# qcow2 format image can be directly used in the integration test.
FOCAL_OS_QCOW2_IMAGE_UNCOMPRESSED_NAME="focal-server-cloudimg-arm64-custom.qcow2"
FOCAL_OS_QCOW2_IMAGE_UNCOMPRESSED_DOWNLOAD_URL="https://cloudhypervisorstorage.blob.core.windows.net/images/$FOCAL_OS_QCOW2_IMAGE_UNCOMPRESSED_NAME"
FOCAL_OS_QCOW2_UNCOMPRESSED_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_QCOW2_IMAGE_UNCOMPRESSED_NAME"
if [ ! -f "$FOCAL_OS_QCOW2_UNCOMPRESSED_IMAGE" ]; then
pushd $WORKLOADS_DIR
time wget --quiet $FOCAL_OS_QCOW2_IMAGE_UNCOMPRESSED_DOWNLOAD_URL || exit 1
popd
fi
ALPINE_MINIROOTFS_URL="http://dl-cdn.alpinelinux.org/alpine/v3.11/releases/aarch64/alpine-minirootfs-3.11.3-aarch64.tar.gz"
ALPINE_MINIROOTFS_TARBALL="$WORKLOADS_DIR/alpine-minirootfs-aarch64.tar.gz"
if [ ! -f "$ALPINE_MINIROOTFS_TARBALL" ]; then
pushd $WORKLOADS_DIR
time wget --quiet $ALPINE_MINIROOTFS_URL -O $ALPINE_MINIROOTFS_TARBALL || exit 1
popd
fi
ALPINE_INITRAMFS_IMAGE="$WORKLOADS_DIR/alpine_initramfs.img"
if [ ! -f "$ALPINE_INITRAMFS_IMAGE" ]; then
pushd $WORKLOADS_DIR
mkdir alpine-minirootfs
tar xf "$ALPINE_MINIROOTFS_TARBALL" -C alpine-minirootfs
cat > alpine-minirootfs/init <<-EOF
#! /bin/sh
mount -t devtmpfs dev /dev
echo \$TEST_STRING > /dev/console
poweroff -f
EOF
chmod +x alpine-minirootfs/init
cd alpine-minirootfs
find . -print0 |
cpio --null --create --verbose --owner root:root --format=newc > "$ALPINE_INITRAMFS_IMAGE"
popd
fi
pushd $WORKLOADS_DIR
sha1sum sha1sums-aarch64 --check
if [ $? -ne 0 ]; then
echo "sha1sum validation of images failed, remove invalid images to fix the issue."
exit 1
fi
popd
# Build custom kernel based on virtio-pmem and virtio-fs upstream patches
PE_IMAGE="$WORKLOADS_DIR/Image"
LINUX_CUSTOM_DIR="$WORKLOADS_DIR/linux-custom"
build_custom_linux_kernel() {
pushd $LINUX_CUSTOM_DIR
time make -j `nproc`
cp arch/arm64/boot/Image $WORKLOADS_DIR/Image || exit 1
popd
}
SRCDIR=$PWD
if [ ! -d "$LINUX_CUSTOM_DIR" ]; then
pushd $WORKLOADS_DIR
time git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b "virtio-fs-virtio-iommu-5.8-rc4" $LINUX_CUSTOM_DIR
cp $SRCDIR/resources/linux-config-aarch64 $LINUX_CUSTOM_DIR/.config
popd
else
pushd $LINUX_CUSTOM_DIR
git fetch
git checkout -f "virtio-fs-virtio-iommu-5.8-rc4"
cp $SRCDIR/resources/linux-config-aarch64 $LINUX_CUSTOM_DIR/.config
popd
fi
build_custom_linux_kernel
VIRTIOFSD="$WORKLOADS_DIR/virtiofsd"
QEMU_DIR="qemu_build"
if [ ! -f "$VIRTIOFSD" ]; then
pushd $WORKLOADS_DIR
git clone --depth 1 "https://gitlab.com/virtio-fs/qemu.git" -b "virtio-fs-dev" $QEMU_DIR
pushd $QEMU_DIR
time ./configure --prefix=$PWD --target-list=aarch64-softmmu
time make virtiofsd -j `nproc`
cp virtiofsd $VIRTIOFSD || exit 1
popd
rm -rf $QEMU_DIR
sudo setcap cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_setgid,cap_setuid,cap_mknod,cap_setfcap,cap_sys_admin+epi "virtiofsd" || exit 1
popd
fi
BLK_IMAGE="$WORKLOADS_DIR/blk.img"
MNT_DIR="mount_image"
if [ ! -f "$BLK_IMAGE" ]; then
pushd $WORKLOADS_DIR
fallocate -l 16M $BLK_IMAGE
mkfs.ext4 -j $BLK_IMAGE
mkdir $MNT_DIR
sudo mount -t ext4 $BLK_IMAGE $MNT_DIR
sudo bash -c "echo bar > $MNT_DIR/foo" || exit 1
sudo umount $BLK_IMAGE
rm -r $MNT_DIR
popd
fi
SHARED_DIR="$WORKLOADS_DIR/shared_dir"
if [ ! -d "$SHARED_DIR" ]; then
mkdir -p $SHARED_DIR
echo "foo" > "$SHARED_DIR/file1"
echo "bar" > "$SHARED_DIR/file3" || exit 1
fi
}
# lock the workloads folder to avoid parallel updating by different containers
(
echo "try to lock $WORKLOADS_DIR folder and update"
flock -x 12 && update_workloads
) 12>$WORKLOADS_LOCK
# Check if there is any error in the execution of `update_workloads`.
# If there is any error, then kill the shell. Otherwise the script will continue
# running even if the `update_workloads` function was failed.
RES=$?
if [ $RES -ne 0 ]; then
exit 1
fi
# Create tap interface without multipe queues support for vhost_user_net test.
sudo ip tuntap add name vunet-tap0 mode tap
# Create tap interface with multipe queues support for vhost_user_net test.
sudo ip tuntap add name vunet-tap1 mode tap multi_queue
BUILD_TARGET="aarch64-unknown-linux-${CH_LIBC}"
CFLAGS=""
TARGET_CC=""
if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then
TARGET_CC="musl-gcc"
CFLAGS="-I /usr/include/aarch64-linux-musl/ -idirafter /usr/include/"
fi
cargo build --all --release --no-default-features --features pci,kvm --target $BUILD_TARGET
strip target/$BUILD_TARGET/release/cloud-hypervisor
strip target/$BUILD_TARGET/release/vhost_user_net
strip target/$BUILD_TARGET/release/ch-remote
# Copy for non-privileged net test
cp target/$BUILD_TARGET/release/cloud-hypervisor target/$BUILD_TARGET/release/cloud-hypervisor-unprivileged
sudo setcap cap_net_admin+ep target/$BUILD_TARGET/release/cloud-hypervisor
sudo setcap cap_net_admin+ep target/$BUILD_TARGET/release/vhost_user_net
# Enable KSM with some reasonable parameters so that it won't take too long
# for the memory to be merged between two processes.
sudo bash -c "echo 1000000 > /sys/kernel/mm/ksm/pages_to_scan"
sudo bash -c "echo 10 > /sys/kernel/mm/ksm/sleep_millisecs"
sudo bash -c "echo 1 > /sys/kernel/mm/ksm/run"
sudo adduser $USER kvm
newgrp kvm << EOF
export RUST_BACKTRACE=1
time cargo test --no-default-features --features "integration_tests,pci,kvm" "tests::parallel::$@" -- --nocapture
EOF
RES=$?
if [ $RES -eq 0 ]; then
# virtio-mmio based testing
cargo build --release --target $BUILD_TARGET --no-default-features --features "mmio,kvm"
strip target/$BUILD_TARGET/release/cloud-hypervisor
strip target/$BUILD_TARGET/release/vhost_user_net
strip target/$BUILD_TARGET/release/ch-remote
sudo setcap cap_net_admin+ep target/$BUILD_TARGET/release/cloud-hypervisor
sudo setcap cap_net_admin+ep target/$BUILD_TARGET/release/vhost_user_net
# Ensure test binary has the same caps as the cloud-hypervisor one
time cargo test --no-run --no-default-features --features "integration_tests,mmio,kvm" -- --nocapture || exit 1
ls target/debug/deps/cloud_hypervisor-* | xargs -n 1 sudo setcap cap_net_admin+ep
newgrp kvm << EOF
export RUST_BACKTRACE=1
time cargo test --no-default-features --features "integration_tests,mmio,kvm" "tests::parallel::$@" -- --nocapture
EOF
RES=$?
fi
# Tear vhost_user_net test network down
sudo ip link del vunet-tap0
sudo ip link del vunet-tap1
exit $RES

View File

@@ -8,7 +8,7 @@ export BUILD_TARGET=${BUILD_TARGET-x86_64-unknown-linux-gnu}
WORKLOADS_DIR="$HOME/workloads"
mkdir -p "$WORKLOADS_DIR"
cp scripts/sha1sums $WORKLOADS_DIR
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/latest | grep "browser_download_url" | grep -o 'https://.*[^ "]')
FW="$WORKLOADS_DIR/hypervisor-fw"
@@ -18,24 +18,7 @@ if [ ! -f "$FW" ]; then
popd
fi
CLEAR_OS_IMAGE_NAME="clear-31311-cloudguest.img"
CLEAR_OS_IMAGE_URL="https://cloudhypervisorstorage.blob.core.windows.net/images/$CLEAR_OS_IMAGE_NAME"
CLEAR_OS_IMAGE="$WORKLOADS_DIR/$CLEAR_OS_IMAGE_NAME"
if [ ! -f "$CLEAR_OS_IMAGE" ]; then
pushd $WORKLOADS_DIR
time wget --quiet $CLEAR_OS_IMAGE_URL || exit 1
popd
fi
CLEAR_OS_RAW_IMAGE_NAME="clear-31311-cloudguest-raw.img"
CLEAR_OS_RAW_IMAGE="$WORKLOADS_DIR/$CLEAR_OS_RAW_IMAGE_NAME"
if [ ! -f "$CLEAR_OS_RAW_IMAGE" ]; then
pushd $WORKLOADS_DIR
time qemu-img convert -p -f qcow2 -O raw $CLEAR_OS_IMAGE_NAME $CLEAR_OS_RAW_IMAGE_NAME || exit 1
popd
fi
BIONIC_OS_IMAGE_NAME="bionic-server-cloudimg-amd64.img"
BIONIC_OS_IMAGE_NAME="bionic-server-cloudimg-amd64.qcow2"
BIONIC_OS_IMAGE_URL="https://cloudhypervisorstorage.blob.core.windows.net/images/$BIONIC_OS_IMAGE_NAME"
BIONIC_OS_IMAGE="$WORKLOADS_DIR/$BIONIC_OS_IMAGE_NAME"
if [ ! -f "$BIONIC_OS_IMAGE" ]; then
@@ -44,7 +27,7 @@ if [ ! -f "$BIONIC_OS_IMAGE" ]; then
popd
fi
BIONIC_OS_RAW_IMAGE_NAME="bionic-server-cloudimg-amd64-raw.img"
BIONIC_OS_RAW_IMAGE_NAME="bionic-server-cloudimg-amd64.raw"
BIONIC_OS_RAW_IMAGE="$WORKLOADS_DIR/$BIONIC_OS_RAW_IMAGE_NAME"
if [ ! -f "$BIONIC_OS_RAW_IMAGE" ]; then
pushd $WORKLOADS_DIR
@@ -53,7 +36,7 @@ if [ ! -f "$BIONIC_OS_RAW_IMAGE" ]; then
fi
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64.img"
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom.qcow2"
FOCAL_OS_IMAGE_URL="https://cloudhypervisorstorage.blob.core.windows.net/images/$FOCAL_OS_IMAGE_NAME"
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
if [ ! -f "$FOCAL_OS_IMAGE" ]; then
@@ -62,7 +45,7 @@ if [ ! -f "$FOCAL_OS_IMAGE" ]; then
popd
fi
FOCAL_OS_RAW_IMAGE_NAME="focal-server-cloudimg-amd64-raw.img"
FOCAL_OS_RAW_IMAGE_NAME="focal-server-cloudimg-amd64-custom.raw"
FOCAL_OS_RAW_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_RAW_IMAGE_NAME"
if [ ! -f "$FOCAL_OS_RAW_IMAGE" ]; then
pushd $WORKLOADS_DIR
@@ -97,7 +80,7 @@ if [ ! -f "$ALPINE_INITRAMFS_IMAGE" ]; then
fi
pushd $WORKLOADS_DIR
sha1sum sha1sums --check
sha1sum sha1sums-x86_64 --check
if [ $? -ne 0 ]; then
echo "sha1sum validation of images failed, remove invalid images to fix the issue."
exit 1
@@ -114,8 +97,8 @@ LINUX_CUSTOM_DIR="$WORKLOADS_DIR/linux-custom"
if [ ! -f "$VMLINUX_IMAGE" ] || [ ! -f "$VMLINUX_PVH_IMAGE" ]; then
SRCDIR=$PWD
pushd $WORKLOADS_DIR
time git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b "virtio-fs-virtio-iommu-virtio-mem-5.6-rc4" $LINUX_CUSTOM_DIR
cp $SRCDIR/resources/linux-config $LINUX_CUSTOM_DIR/.config
time git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b "virtio-fs-virtio-iommu-5.8-rc4" $LINUX_CUSTOM_DIR
cp $SRCDIR/resources/linux-config-x86_64 $LINUX_CUSTOM_DIR/.config
popd
fi
@@ -144,14 +127,14 @@ VIRTIOFSD="$WORKLOADS_DIR/virtiofsd"
QEMU_DIR="qemu_build"
if [ ! -f "$VIRTIOFSD" ]; then
pushd $WORKLOADS_DIR
git clone --depth 1 "https://github.com/sboeuf/qemu.git" -b "virtio-fs" $QEMU_DIR
git clone --depth 1 "https://gitlab.com/virtio-fs/qemu.git" -b "virtio-fs-dev" $QEMU_DIR
pushd $QEMU_DIR
time ./configure --prefix=$PWD --target-list=x86_64-softmmu
time make virtiofsd -j `nproc`
cp virtiofsd $VIRTIOFSD || exit 1
popd
rm -rf $QEMU_DIR
sudo setcap cap_dac_override,cap_sys_admin+epi "virtiofsd" || exit 1
sudo setcap cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_setgid,cap_setuid,cap_mknod,cap_setfcap,cap_sys_admin+epi "virtiofsd" || exit 1
popd
fi
@@ -179,7 +162,7 @@ fi
VFIO_DIR="$WORKLOADS_DIR/vfio"
rm -rf $VFIO_DIR
mkdir -p $VFIO_DIR
cp $CLEAR_OS_IMAGE $VFIO_DIR
cp $FOCAL_OS_IMAGE $VFIO_DIR
cp $FW $VFIO_DIR
cp $VMLINUX_IMAGE $VFIO_DIR || exit 1
@@ -218,7 +201,7 @@ TARGET_CC="musl-gcc"
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
fi
cargo build --release --target $BUILD_TARGET
cargo build --all --release --target $BUILD_TARGET
strip target/$BUILD_TARGET/release/cloud-hypervisor
strip target/$BUILD_TARGET/release/vhost_user_net
strip target/$BUILD_TARGET/release/ch-remote
@@ -239,24 +222,34 @@ sudo bash -c "echo 1000000 > /sys/kernel/mm/ksm/pages_to_scan"
sudo bash -c "echo 10 > /sys/kernel/mm/ksm/sleep_millisecs"
sudo bash -c "echo 1 > /sys/kernel/mm/ksm/run"
# Ensure test binary has the same caps as the cloud-hypervisor one
time cargo test --no-run --features "integration_tests" -- --nocapture || exit 1
ls target/debug/deps/cloud_hypervisor-* | xargs -n 1 sudo setcap cap_net_admin+ep
# test_vfio relies on hugepages
echo 4096 | sudo tee /proc/sys/vm/nr_hugepages
sudo chmod a+rwX /dev/hugepages
# Ensure test binary has the same caps as the cloud-hypervisor one
time cargo test --no-run --features "integration_tests" -- --nocapture || exit 1
ls target/debug/deps/cloud_hypervisor-* | xargs -n 1 sudo setcap cap_net_admin+ep
sudo adduser $USER kvm
newgrp kvm << EOF
export RUST_BACKTRACE=1
time cargo test --features "integration_tests" "$@" -- --nocapture
time cargo test --features "integration_tests" "tests::parallel::$@" -- --nocapture
EOF
RES=$?
# Run some tests in sequence since the result could be affected by other tests
# running in parallel.
if [ $RES -eq 0 ]; then
newgrp kvm << EOF
export RUST_BACKTRACE=1
time cargo test --features "integration_tests" "tests::sequential::$@" -- --nocapture --test-threads=1
EOF
RES=$?
fi
if [ $RES -eq 0 ]; then
# virtio-mmio based testing
cargo build --release --target $BUILD_TARGET --no-default-features --features "mmio"
cargo build --all --release --target $BUILD_TARGET --no-default-features --features "mmio,kvm"
strip target/$BUILD_TARGET/release/cloud-hypervisor
strip target/$BUILD_TARGET/release/vhost_user_net
strip target/$BUILD_TARGET/release/ch-remote
@@ -269,10 +262,20 @@ if [ $RES -eq 0 ]; then
newgrp kvm << EOF
export RUST_BACKTRACE=1
time cargo test --features "integration_tests,mmio" "$@" -- --nocapture
time cargo test --features "integration_tests,mmio" "tests::parallel::$@" -- --nocapture
EOF
RES=$?
# Run some tests in sequence since the result could be affected by other tests
# running in parallel.
if [ $RES -eq 0 ]; then
newgrp kvm << EOF
export RUST_BACKTRACE=1
time cargo test --features "integration_tests,mmio" "tests::sequential::$@" -- --nocapture --test-threads=1
EOF
RES=$?
fi
fi
# Tear VFIO test network down

View File

@@ -3,8 +3,12 @@
source $HOME/.cargo/env
BUILD_TARGET=${BUILD_TARGET-x86_64-unknown-linux-gnu}
cargo_args=("$@")
[ $(uname -m) = "aarch64" ] && cargo_args+=("--no-default-features")
[ $(uname -m) = "aarch64" ] && cargo_args+=("--features mmio,kvm")
[ $(uname -m) = "aarch64" ] && sed -i 's/"with-serde",\ //g' hypervisor/Cargo.toml
cargo test --target $BUILD_TARGET --workspace --no-run
cargo test --target $BUILD_TARGET --workspace --no-run ${cargo_args[@]}
pushd target/$BUILD_TARGET/debug
ls | grep net_util | grep -v "\.d" | xargs -n 1 sudo setcap cap_net_admin,cap_net_raw+ep
popd
@@ -12,5 +16,5 @@ popd
sudo adduser $USER kvm
newgrp kvm << EOF || exit 1
export RUST_BACKTRACE=1
cargo test --target $BUILD_TARGET --workspace "$@" || exit 1;
cargo test --target $BUILD_TARGET --workspace ${cargo_args[@]} || exit 1;
EOF

View File

@@ -1,7 +0,0 @@
cf7cfa783082fc4d6b4d1c0a53e4402648c14b82 clear-31311-cloudguest.img
142a410546b592ff9536b46bb410faf8ac11edee clear-31311-cloudguest-raw.img
27f3b17962ace69b51f0ddc2012095e3109e6ed8 bionic-server-cloudimg-amd64.img
8db9cc58b01452ce2d06c313177e6e74d8582d93 bionic-server-cloudimg-amd64-raw.img
d4a44acc6014d5f83dea1c625c43d677a95fa75f alpine-minirootfs-x86_64.tar.gz
3cab64475048383e70a38550734e98961cfe0fd6 focal-server-cloudimg-amd64-raw.img
c74a95665b189f93cb16a2c1770820c6a6a43fb0 focal-server-cloudimg-amd64.img

6
scripts/sha1sums-aarch64 Normal file
View File

@@ -0,0 +1,6 @@
6fee67adbfed8db7a225be23ee9d90b5bd7f19e6 bionic-server-cloudimg-arm64.img
786fe1c33588334e92b35c65e414da068df180bc bionic-server-cloudimg-arm64.raw
34039f2fc4242acff0d42af82fbee91070978da3 bionic-server-cloudimg-arm64.qcow2
e4addb6e212a298144f9eb0eb6e36019d013f0e7 alpine-minirootfs-aarch64.tar.gz
063d1a9f32db641138e6e0c53f58e6545a3d48e6 focal-server-cloudimg-arm64-custom.raw
013837926872f1c7d1b3a1a95305e4a31c8600ab focal-server-cloudimg-arm64-custom.qcow2

5
scripts/sha1sums-x86_64 Normal file
View File

@@ -0,0 +1,5 @@
27f3b17962ace69b51f0ddc2012095e3109e6ed8 bionic-server-cloudimg-amd64.qcow2
8db9cc58b01452ce2d06c313177e6e74d8582d93 bionic-server-cloudimg-amd64.raw
d4a44acc6014d5f83dea1c625c43d677a95fa75f alpine-minirootfs-x86_64.tar.gz
f131563d3aa466cb5d2c25ffe7007c9cb8d15818 focal-server-cloudimg-amd64-custom.qcow2
d5a08d2b35d48042d2c08fdc0c31ae656073f43e focal-server-cloudimg-amd64-custom.raw

View File

@@ -23,6 +23,7 @@ enum Error {
ServerResponse(StatusCode, Option<String>),
InvalidCPUCount(std::num::ParseIntError),
InvalidMemorySize(std::num::ParseIntError),
InvalidBalloonSize(std::num::ParseIntError),
AddDeviceConfig(vmm::config::Error),
AddDiskConfig(vmm::config::Error),
AddFsConfig(vmm::config::Error),
@@ -49,6 +50,7 @@ impl fmt::Display for Error {
}
InvalidCPUCount(e) => write!(f, "Error parsing CPU count: {}", e),
InvalidMemorySize(e) => write!(f, "Error parsing memory size: {}", e),
InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {}", e),
AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {}", e),
AddDiskConfig(e) => write!(f, "Error parsing disk syntax: {}", e),
AddFsConfig(e) => write!(f, "Error parsing filesystem syntax: {}", e),
@@ -204,6 +206,7 @@ fn resize_api_command(
socket: &mut UnixStream,
cpus: Option<&str>,
memory: Option<&str>,
balloon: Option<&str>,
) -> Result<(), Error> {
let desired_vcpus: Option<u8> = if let Some(cpus) = cpus {
Some(cpus.parse().map_err(Error::InvalidCPUCount)?)
@@ -217,9 +220,16 @@ fn resize_api_command(
None
};
let desired_ram_w_balloon: Option<u64> = if let Some(balloon) = balloon {
Some(balloon.parse().map_err(Error::InvalidBalloonSize)?)
} else {
None
};
let resize = vmm::api::VmResizeData {
desired_vcpus,
desired_ram,
desired_ram_w_balloon,
};
simple_api_command(
@@ -337,6 +347,7 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
match matches.subcommand_name() {
Some("info") => simple_api_command(&mut socket, "GET", "info", None),
Some("counters") => simple_api_command(&mut socket, "GET", "counters", None),
Some("resize") => resize_api_command(
&mut socket,
matches
@@ -347,6 +358,10 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
.subcommand_matches("resize")
.unwrap()
.value_of("memory"),
matches
.subcommand_matches("resize")
.unwrap()
.value_of("balloon"),
),
Some("add-device") => add_device_api_command(
&mut socket,
@@ -498,6 +513,7 @@ fn main() {
.arg(Arg::with_name("id").index(1).help("<device_id>")),
)
.subcommand(SubCommand::with_name("info").about("Info on the VM"))
.subcommand(SubCommand::with_name("counters").about("Counters from the VM"))
.subcommand(SubCommand::with_name("pause").about("Pause the VM"))
.subcommand(SubCommand::with_name("reboot").about("Reboot the VM"))
.subcommand(
@@ -516,6 +532,13 @@ fn main() {
.help("New memory size (in MiB)")
.takes_value(true)
.number_of_values(1),
)
.arg(
Arg::with_name("balloon")
.long("balloon")
.help("New memory with balloon size")
.takes_value(true)
.number_of_values(1),
),
)
.subcommand(SubCommand::with_name("resume").about("Resume the VM"))

View File

@@ -12,7 +12,7 @@ extern crate clap;
use clap::{App, Arg, ArgGroup, ArgMatches};
use libc::EFD_NONBLOCK;
use log::LevelFilter;
use seccomp::SeccompLevel;
use seccomp::SeccompAction;
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use std::{env, process};
@@ -78,7 +78,12 @@ fn create_app<'a, 'b>(
default_rng: &'a str,
api_server_path: &'a str,
) -> App<'a, 'b> {
App::new("cloud-hypervisor")
#[cfg(target_arch = "x86_64")]
let mut app: App;
#[cfg(target_arch = "aarch64")]
let app: App;
app = App::new("cloud-hypervisor")
// 'BUILT_VERSION' is set by the build script 'build.rs' at
// compile time
.version(env!("BUILT_VERSION"))
@@ -90,7 +95,10 @@ fn create_app<'a, 'b>(
.arg(
Arg::with_name("cpus")
.long("cpus")
.help("Number of virtual CPUs")
.help(
"boot=<boot_vcpus>,max=<max_vcpus>,\
topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>",
)
.default_value(&default_vcpus)
.group("vm-config"),
)
@@ -252,9 +260,23 @@ fn create_app<'a, 'b>(
Arg::with_name("seccomp")
.long("seccomp")
.takes_value(true)
.possible_values(&["true", "false"])
.possible_values(&["true", "false", "log"])
.default_value("true"),
)
);
#[cfg(target_arch = "x86_64")]
{
app = app.arg(
Arg::with_name("sgx-epc")
.long("sgx-epc")
.help(config::SgxEpcConfig::SYNTAX)
.takes_value(true)
.min_values(1)
.group("vm-config"),
);
}
app
}
fn start_vmm(cmd_arguments: ArgMatches) {
@@ -266,27 +288,28 @@ fn start_vmm(cmd_arguments: ArgMatches) {
let api_evt = EventFd::new(EFD_NONBLOCK).expect("Cannot create API EventFd");
let http_sender = api_request_sender.clone();
let seccomp_level = if let Some(seccomp_value) = cmd_arguments.value_of("seccomp") {
let seccomp_action = if let Some(seccomp_value) = cmd_arguments.value_of("seccomp") {
match seccomp_value {
"true" => SeccompLevel::Advanced,
"false" => SeccompLevel::None,
"true" => SeccompAction::Trap,
"false" => SeccompAction::Allow,
"log" => SeccompAction::Log,
_ => {
eprintln!("Invalid parameter {} for \"--seccomp\" flag", seccomp_value);
process::exit(1);
}
}
} else {
SeccompLevel::Advanced
SeccompAction::Trap
};
let hypervisor = hypervisor::new().unwrap();
let vmm_thread = match vmm::start_vmm_thread(
env!("CARGO_PKG_VERSION").to_string(),
api_socket_path,
api_evt.try_clone().unwrap(),
http_sender,
api_request_receiver,
&seccomp_level,
&seccomp_action,
hypervisor,
) {
Ok(t) => t,
Err(e) => {
@@ -487,6 +510,7 @@ mod unit_tests {
cpus: CpusConfig {
boot_vcpus: 1,
max_vcpus: 1,
topology: None,
},
memory: MemoryConfig {
size: 536_870_912,
@@ -496,6 +520,8 @@ mod unit_tests {
hotplug_size: None,
shared: false,
hugepages: false,
balloon: false,
balloon_size: 0,
},
kernel: Some(KernelConfig {
path: PathBuf::from("/path/to/kernel"),
@@ -525,6 +551,8 @@ mod unit_tests {
devices: None,
vsock: None,
iommu: false,
#[cfg(target_arch = "x86_64")]
sgx_epc: None,
};
aver_eq!(tb, expected_vm_config, result_vm_config);
@@ -908,6 +936,7 @@ mod unit_tests {
}"#,
true,
),
#[cfg(target_arch = "x86_64")]
(
vec![
"cloud-hypervisor", "--kernel", "/path/to/kernel",
@@ -922,6 +951,7 @@ mod unit_tests {
}"#,
false,
),
#[cfg(target_arch = "x86_64")]
(
vec![
"cloud-hypervisor", "--kernel", "/path/to/kernel",
@@ -1201,6 +1231,7 @@ mod unit_tests {
}"#,
true,
),
#[cfg(target_arch = "x86_64")]
(
vec![
"cloud-hypervisor",
@@ -1218,6 +1249,7 @@ mod unit_tests {
}"#,
true,
),
#[cfg(target_arch = "x86_64")]
(
vec![
"cloud-hypervisor",
@@ -1234,6 +1266,7 @@ mod unit_tests {
}"#,
false,
),
#[cfg(target_arch = "x86_64")]
(
vec![
"cloud-hypervisor",
@@ -1250,6 +1283,7 @@ mod unit_tests {
}"#,
true,
),
#[cfg(target_arch = "x86_64")]
(
vec![
"cloud-hypervisor",
@@ -1325,6 +1359,7 @@ mod unit_tests {
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_valid_vm_config_devices() {
vec![
(
@@ -1449,6 +1484,7 @@ mod unit_tests {
}"#,
false,
),
#[cfg(target_arch = "x86_64")]
(
vec![
"cloud-hypervisor",
@@ -1464,6 +1500,7 @@ mod unit_tests {
}"#,
true,
),
#[cfg(target_arch = "x86_64")]
(
vec![
"cloud-hypervisor",

View File

@@ -1,3 +0,0 @@
{
"hostname" : "cloud"
}

View File

@@ -1,80 +0,0 @@
#cloud-config
users:
- name: cloud
passwd: $6$7125787751a8d18a$sHwGySomUA1PawiNFWVCKYQN.Ec.Wzz0JtPPL1MvzFrkwmop2dq7.4CYf03A5oemPQ4pOFCCrtCelvFBEle/K.
sudo:
- ALL=(ALL) NOPASSWD:ALL
write_files:
-
path: /etc/systemd/network/00-static-l1.network
permissions: 0644
content: |
[Match]
MACAddress=12:34:56:78:90:ab
[Network]
Address=192.168.2.2/24
Gateway=192.168.2.1
-
path: /etc/systemd/network/00-static-l2-1.network
permissions: 0644
content: |
[Match]
MACAddress=de:ad:be:ef:12:34
[Network]
Address=192.168.2.3/24
Gateway=192.168.2.1
-
path: /etc/systemd/network/00-static-l2-2.network
permissions: 0644
content: |
[Match]
MACAddress=de:ad:be:ef:34:56
[Network]
Address=192.168.2.4/24
Gateway=192.168.2.1
-
path: /etc/systemd/network/00-static-l2-3.network
permissions: 0644
content: |
[Match]
MACAddress=de:ad:be:ef:56:78
[Network]
Address=192.168.2.5/24
Gateway=192.168.2.1
-
path: /etc/systemd/system/vfio.service
permissions: 0644
content: |
[Unit]
Description=VFIO test systemd service
[Service]
Type=simple
ExecStart=/bin/bash /usr/bin/cloud-hypervisor-vfio.sh
[Install]
WantedBy=multi-user.target
-
path: /usr/bin/cloud-hypervisor-vfio.sh
permissions: 0755
content: |
#!/bin/bash
mount -t virtiofs -o dax myfs /mnt
bash -c "echo 0000:00:05.0 > /sys/bus/pci/devices/0000\:00\:05.0/driver/unbind"
bash -c "echo 1af4 1041 > /sys/bus/pci/drivers/vfio-pci/new_id"
bash -c "echo 0000:00:06.0 > /sys/bus/pci/devices/0000\:00\:06.0/driver/unbind"
bash -c "echo 1af4 1041 > /sys/bus/pci/drivers/vfio-pci/new_id"
# 1G ram requires 512 pages
echo 512 | sudo tee /proc/sys/vm/nr_hugepages
sudo chmod a+rwX /dev/hugepages
/mnt/cloud-hypervisor --kernel /mnt/vmlinux --cmdline "console=hvc0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=PARTUUID=6fb4d1a8-6c8c-4dd7-9f7c-1fe0b9f2574c VFIOTAG" --disk path=/mnt/clear-31311-cloudguest.img path=/mnt/cloudinit.img --cpus boot=1 --memory size=512M,hotplug_size=1G,file=/dev/hugepages --device path=/sys/bus/pci/devices/0000:00:05.0/ path=/sys/bus/pci/devices/0000:00:06.0/ --api-socket /tmp/ch_api.sock

View File

@@ -1,12 +1,26 @@
network:
version: 1
config:
- type: physical
name: eth0
mac_address: 12:34:56:78:90:ab
subnets:
- type: static
address: 192.168.2.2/24
gateway: 192.168.2.1
dns_nameservers:
- 192.168.2.1
version: 2
ethernets:
id0:
match:
macaddress: 12:34:56:78:90:ab
addresses:
- 192.168.2.2/24
gateway4: 192.168.2.1
id1:
match:
macaddress: de:ad:be:ef:12:34
addresses:
- 192.168.2.3/24
gateway4: 192.168.2.1
id2:
match:
macaddress: de:ad:be:ef:34:56
addresses:
- 192.168.2.4/24
gateway4: 192.168.2.1
id3:
match:
macaddress: de:ad:be:ef:56:78
addresses:
- 192.168.2.5/24
gateway4: 192.168.2.1

View File

@@ -7,4 +7,35 @@ users:
inactive: False
shell: /bin/bash
ssh_pwauth: True
ssh_pwauth: True
write_files:
-
path: /etc/systemd/system/vfio.service
permissions: 0644
content: |
[Unit]
Description=VFIO test systemd service
[Service]
Type=simple
ExecStart=/bin/bash /usr/bin/cloud-hypervisor-vfio.sh
[Install]
WantedBy=multi-user.target
-
path: /usr/bin/cloud-hypervisor-vfio.sh
permissions: 0755
content: |
#!/bin/bash
mount -t virtiofs -o dax myfs /mnt
bash -c "echo 0000:00:05.0 > /sys/bus/pci/devices/0000\:00\:05.0/driver/unbind"
bash -c "echo 1af4 1041 > /sys/bus/pci/drivers/vfio-pci/new_id"
bash -c "echo 0000:00:06.0 > /sys/bus/pci/devices/0000\:00\:06.0/driver/unbind"
bash -c "echo 1af4 1041 > /sys/bus/pci/drivers/vfio-pci/new_id"
# 1G ram requires 512 pages
echo 512 | sudo tee /proc/sys/vm/nr_hugepages
sudo chmod a+rwX /dev/hugepages
/mnt/cloud-hypervisor --kernel /mnt/vmlinux --cmdline "console=hvc0 reboot=k panic=1 nomodules i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd root=/dev/vda1 VFIOTAG" --disk path=/mnt/focal-server-cloudimg-amd64-custom.qcow2 path=/mnt/cloudinit.img --cpus boot=1 --memory size=512M,hotplug_size=1G,file=/dev/hugepages --device path=/sys/bus/pci/devices/0000:00:05.0/ path=/sys/bus/pci/devices/0000:00:06.0/ --api-socket /tmp/ch_api.sock

File diff suppressed because it is too large Load Diff

View File

@@ -6,13 +6,11 @@ edition = "2018"
[features]
default = []
pci_support = ["vm-virtio/pci_support"]
mmio_support = ["vm-virtio/mmio_support"]
[dependencies]
epoll = ">=4.0.1"
libc = "0.2.71"
log = "0.4.8"
libc = "0.2.74"
log = "0.4.11"
virtio-bindings = "0.1.0"
vm-memory = "0.2.1"
vm-virtio = { path = "../vm-virtio" }

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