diff --git a/.buildkite/common.py b/.buildkite/common.py index 196435bb2e0..24607609841 100644 --- a/.buildkite/common.py +++ b/.buildkite/common.py @@ -6,6 +6,7 @@ """ import argparse +import ast import json import os import random @@ -13,14 +14,15 @@ import subprocess from pathlib import Path -DEFAULT_INSTANCES = { - "c5n.metal": "x86_64", # Intel Skylake - "m5n.metal": "x86_64", # Intel Cascade Lake - "m6i.metal": "x86_64", # Intel Icelake - "m6a.metal": "x86_64", # AMD Milan - "m6g.metal": "aarch64", # Graviton2 - "m7g.metal": "aarch64", # Graviton3 -} +DEFAULT_INSTANCES = [ + "c5n.metal", # Intel Skylake + "m5n.metal", # Intel Cascade Lake + "m6i.metal", # Intel Icelake + "m6a.metal", # AMD Milan + "m7a.metal-48xl", # AMD Genoa + "m6g.metal", # Graviton2 + "m7g.metal", # Graviton3 +] DEFAULT_PLATFORMS = [ ("al2", "linux_5.10"), @@ -28,6 +30,11 @@ ] +def get_arch_for_instance(instance): + """Return instance architecture""" + return "x86_64" if instance[2] != "g" else "aarch64" + + def overlay_dict(base: dict, update: dict): """Overlay a dict over a base one""" base = base.copy() @@ -133,7 +140,8 @@ def __call__(self, parser, namespace, value, option_string=None): res = getattr(namespace, self.dest, {}) key_str, val = value.split("=", maxsplit=1) keys = key_str.split("/") - update = {keys[-1]: val} + # Interpret it as a literal iff it starts like one + update = {keys[-1]: ast.literal_eval(val) if val[0] in "[{'" else val} for key in list(reversed(keys))[1:]: update = {key: update} res = overlay_dict(res, update) @@ -145,7 +153,7 @@ def __call__(self, parser, namespace, value, option_string=None): "--instances", required=False, nargs="+", - default=DEFAULT_INSTANCES.keys(), + default=DEFAULT_INSTANCES, ) COMMON_PARSER.add_argument( "--platforms", @@ -171,6 +179,12 @@ def __call__(self, parser, namespace, value, option_string=None): default=None, type=str, ) +COMMON_PARSER.add_argument( + "--no-kani", + help="Don't add kani step", + action="store_true", + default=False, +) def random_str(k: int): @@ -243,7 +257,7 @@ def __init__(self, with_build_step=True, **kwargs): self.per_instance = overlay_dict(per_instance, args.step_param) self.per_arch = self.per_instance.copy() self.per_arch["instances"] = ["m6i.metal", "m7g.metal"] - self.per_arch["platforms"] = [("al2", "linux_5.10")] + self.per_arch["platforms"] = [("al2023", "linux_6.1")] self.binary_dir = args.binary_dir # Build sharing if with_build_step: @@ -288,7 +302,7 @@ def _adapt_group(self, group): step["command"] = prepend + step["command"] if self.shared_build is not None: step["depends_on"] = self.build_key( - DEFAULT_INSTANCES[step["agents"]["instance"]] + get_arch_for_instance(step["agents"]["instance"]) ) return group @@ -323,7 +337,7 @@ def build_group_per_arch(self, label, *args, **kwargs): if set_key: for step in grp["steps"]: step["key"] = self.build_key( - DEFAULT_INSTANCES[step["agents"]["instance"]] + get_arch_for_instance(step["agents"]["instance"]) ) return self.add_step(grp, depends_on_build=depends_on_build) diff --git a/.buildkite/pipeline_cpu_template.py b/.buildkite/pipeline_cpu_template.py index 72a67ea5e7d..e42ae926d2e 100755 --- a/.buildkite/pipeline_cpu_template.py +++ b/.buildkite/pipeline_cpu_template.py @@ -6,7 +6,7 @@ from enum import Enum -from common import DEFAULT_INSTANCES, DEFAULT_PLATFORMS, BKPipeline, group +from common import DEFAULT_PLATFORMS, BKPipeline, group class BkStep(str, Enum): @@ -26,16 +26,19 @@ class BkStep(str, Enum): "tools/devtool -y test --no-build -- -m nonci -n4 --dist worksteal integration_tests/functional/test_cpu_features_x86_64.py -k 'test_cpu_rdmsr' " ], BkStep.LABEL: "📖 rdmsr", - "instances": ["c5n.metal", "m5n.metal", "m6a.metal", "m6i.metal"], - "platforms": DEFAULT_PLATFORMS, + "instances": [ + "c5n.metal", + "m5n.metal", + "m6i.metal", + "m6a.metal", + "m7a.metal-48xl", + ], }, "fingerprint": { BkStep.COMMAND: [ "tools/devtool -y test --no-build -- -m no_block_pr integration_tests/functional/test_cpu_template_helper.py -k test_guest_cpu_config_change", ], BkStep.LABEL: "🖐️ fingerprint", - "instances": DEFAULT_INSTANCES.keys(), - "platforms": DEFAULT_PLATFORMS, }, "cpuid_wrmsr": { "snapshot": { @@ -62,7 +65,13 @@ class BkStep(str, Enum): "c5n.metal": ["m5n.metal", "m6i.metal"], "m6i.metal": ["m5n.metal", "c5n.metal"], }, - "instances": ["c5n.metal", "m5n.metal", "m6i.metal", "m6a.metal"], + "instances": [ + "c5n.metal", + "m5n.metal", + "m6i.metal", + "m6a.metal", + "m7a.metal-48xl", + ], }, } diff --git a/.buildkite/pipeline_cross.py b/.buildkite/pipeline_cross.py index ceb89d27f3d..c611f87c065 100755 --- a/.buildkite/pipeline_cross.py +++ b/.buildkite/pipeline_cross.py @@ -18,13 +18,21 @@ per_instance = pipeline.per_instance.copy() per_instance.pop("instances") per_instance.pop("platforms") - instances_x86_64 = ["c5n.metal", "m5n.metal", "m6i.metal", "m6a.metal"] + instances_x86_64 = [ + "c5n.metal", + "m5n.metal", + "m6i.metal", + "m6a.metal", + "m7a.metal-48xl", + ] instances_aarch64 = ["m7g.metal"] commands = [ - "./tools/devtool -y sh ./tools/create_snapshot_artifact/main.py", - "mkdir -pv snapshots/{instance}_{kv}", - "sudo chown -Rc $USER: snapshot_artifacts", - "mv -v snapshot_artifacts/* snapshots/{instance}_{kv}", + "./tools/devtool -y test --no-build -- -m nonci -n4 integration_tests/functional/test_snapshot_phase1.py", + # punch holes in mem snapshot tiles and tar them so they are preserved in S3 + "find test_results/test_snapshot_phase1 -type f -name mem |xargs -P4 -t -n1 fallocate -d", + "mv -v test_results/test_snapshot_phase1 snapshot_artifacts", + "mkdir -pv snapshots", + "tar cSvf snapshots/{instance}_{kv}.tar snapshot_artifacts", ] pipeline.build_group( "📸 create snapshots", @@ -80,10 +88,10 @@ k_val = pytest_keyword_for_instance.get(dst_instance, "") step = { "command": [ - f"buildkite-agent artifact download snapshots/{src_instance}_{src_kv}/* .", - f"mv -v snapshots/{src_instance}_{src_kv} snapshot_artifacts", + f"buildkite-agent artifact download snapshots/{src_instance}_{src_kv}.tar .", + f"tar xSvf snapshots/{src_instance}_{src_kv}.tar", *pipeline.devtool_test( - pytest_opts=f"-m nonci {k_val} integration_tests/functional/test_snapshot_restore_cross_kernel.py", + pytest_opts=f"-m nonci -n8 --dist worksteal {k_val} integration_tests/functional/test_snapshot_restore_cross_kernel.py", ), ], "label": f"🎬 {src_instance} {src_kv} ➡️ {dst_instance} {dst_kv}", diff --git a/.buildkite/pipeline_perf.py b/.buildkite/pipeline_perf.py index e8169bfb2cd..a94c13b8af0 100755 --- a/.buildkite/pipeline_perf.py +++ b/.buildkite/pipeline_perf.py @@ -121,10 +121,7 @@ # } # will pin steps running on instances "m6i.metal" with kernel version tagged "linux_6.1" # to a new kernel version tagged "linux_6.1-pinned" -pins = { - # TODO: Unpin when performance instability on m6i/5.10 has gone. - "linux_5.10-pinned": {"instance": "m6i.metal", "kv": "linux_5.10"}, -} +pins = {} def apply_pins(steps): diff --git a/.buildkite/pipeline_pr.py b/.buildkite/pipeline_pr.py index 618aa17860b..7f85f777c6b 100755 --- a/.buildkite/pipeline_pr.py +++ b/.buildkite/pipeline_pr.py @@ -48,8 +48,10 @@ "./tools/devtool -y make_release", ) -if not changed_files or any( - x.suffix in [".rs", ".toml", ".lock"] for x in changed_files +if not pipeline.args.no_kani and ( + not changed_files + or any(x.suffix in [".rs", ".toml", ".lock"] for x in changed_files) + or any(x.parent.name == "devctr" for x in changed_files) ): kani_grp = pipeline.build_group( "🔍 Kani", @@ -57,7 +59,7 @@ # Kani step default # Kani runs fastest on m6a.metal instances=["m6a.metal", "m7g.metal"], - platforms=[("al2", "linux_5.10")], + platforms=[("al2023", "linux_6.1")], timeout_in_minutes=300, **DEFAULTS_PERF, depends_on_build=False, @@ -76,7 +78,7 @@ pipeline.build_group( "⚙ Functional and security 🔒", pipeline.devtool_test( - pytest_opts="-n 8 --dist worksteal integration_tests/{{functional,security}}", + pytest_opts="-n 16 --dist worksteal integration_tests/{{functional,security}}", ), ) diff --git a/.cargo/audit.toml b/.cargo/audit.toml index b28486dffb9..6b623cb495c 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -1 +1,7 @@ [advisories] +# The `paste` dependency is transitively included via `gdbstub`. +# While the crate is archived/unmaintained, the author considers it feature-complete +# and functionally stable. gdbstub will be update once they migrate +# to an alternative solution. +# See https://github.com/daniel5151/gdbstub/issues/168 +ignore = ["RUSTSEC-2024-0436"] diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1d6da74ba72..e05f0c2ae15 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -15,16 +15,20 @@ Certificate of Origin and signing off your commits, please check ## PR Checklist +- [ ] I have read and understand [CONTRIBUTING.md][3]. +- [ ] I have run `tools/devtool checkstyle` to verify that the PR passes the + automated style checks. +- [ ] I have described what is done in these changes, why they are needed, and + how they are solving the problem in a clear and encompassing way. +- [ ] I have updated any relevant documentation (both in code and in the docs) + in the PR. +- [ ] I have mentioned all user-facing changes in `CHANGELOG.md`. - [ ] If a specific issue led to this PR, this PR closes the issue. -- [ ] The description of changes is clear and encompassing. -- [ ] Any required documentation changes (code and docs) are included in this - PR. -- [ ] API changes follow the [Runbook for Firecracker API changes][2]. -- [ ] User-facing changes are mentioned in `CHANGELOG.md`. -- [ ] All added/changed functionality is tested. -- [ ] New `TODO`s link to an issue. -- [ ] Commits meet - [contribution quality standards](https://github.com/firecracker-microvm/firecracker/blob/main/CONTRIBUTING.md#contribution-quality-standards). +- [ ] When making API changes, I have followed the + [Runbook for Firecracker API changes][2]. +- [ ] I have tested all new and changed functionalities in unit tests and/or + integration tests. +- [ ] I have linked an issue to every new `TODO`. ______________________________________________________________________ diff --git a/.gitlint b/.gitlint index 8840b19bfe8..3b0cc0e07a0 100644 --- a/.gitlint +++ b/.gitlint @@ -9,8 +9,8 @@ line-length=72 [ignore-body-lines] # Ignore HTTP reference links -# Ignore lines that start with 'Co-Authored-By' or with 'Signed-off-by' -regex=(^\[.+\]: http.+)|(^Co-Authored-By)|(^Signed-off-by) +# Ignore lines that start with 'Co-Authored-By', with 'Signed-off-by' or with 'Fixes' +regex=(^\[.+\]: http.+)|(^Co-Authored-By)|(^Signed-off-by)|(^Fixes:) [ignore-by-author-name] # Ignore certain rules for commits of which the author name matches a regex diff --git a/CHANGELOG.md b/CHANGELOG.md index b0ab56b4db0..0fc97cac00f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,121 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## \[Unreleased\] +## [Unreleased] + +### Added + +- [#5048](https://github.com/firecracker-microvm/firecracker/pull/5048): Added + support for [PVH boot mode](docs/pvh.md). This is used when an x86 kernel + provides the appropriate ELF Note to indicate that PVH boot mode is supported. + Linux kernels newer than 5.0 compiled with `CONFIG_PVH=y` set this ELF Note, + as do FreeBSD kernels. +- [#5065](https://github.com/firecracker-microvm/firecracker/pull/5065) Added + support for Intel AMX (Advanced Matrix Extensions). +- [#4731](https://github.com/firecracker-microvm/firecracker/pull/4731): Added + support for modifying the host TAP device name during snapshot restore. + +### Changed + +- [#5118](https://github.com/firecracker-microvm/firecracker/pull/5118): Cleared + WAITPKG CPUID bit in CPUID normalization. The feature enables a guest to put a + physical processor into an idle state, which is undesirable in a FaaS + environment since that is what the host wants to decide. + +### Deprecated + +- [#4948](https://github.com/firecracker-microvm/firecracker/pull/4948): + Deprecated the `page_size_kib` field in the + [UFFD handshake](docs/snapshotting/handling-page-faults-on-snapshot-resume.md#registering-memory-to-be-handled-via-userfault-file-descriptors), + and replaced it with a `page_size` field. The `page_size_kib` field is + misnamed, as the value Firecracker sets it to is actually the page size in + _bytes_, not KiB. It will be removed in Firecracker 2.0. + +### Removed + +### Fixed + +- #\[[5074](https://github.com/firecracker-microvm/firecracker/pull/5074)\] Fix + the `SendCtrlAltDel` command not working for ACPI-enabled guest kernels, by + dropping the i8042.nopnp argument from the default kernel command line + Firecracker constructs. +- [#5122](https://github.com/firecracker-microvm/firecracker/pull/5122): Keep + the UFFD Unix domain socket open to prevent the race condition between the + guest memory mappings message and the shutdown event that was sometimes + causing arrival of an empty message on the UFFD handler side. + +## [1.11.0] + +### Added + +- [#4987](https://github.com/firecracker-microvm/firecracker/pull/4987): Reset + physical counter register (`CNTPCT_EL0`) on VM startup. This avoids VM reading + the host physical counter value. This is only possible on 6.4 and newer + kernels. For older kernels physical counter will still be passed to the guest + unmodified. See more info + [here](https://github.com/firecracker-microvm/firecracker/blob/main/docs/prod-host-setup.md#arm-only-vm-physical-counter-behaviour) +- [#5088](https://github.com/firecracker-microvm/firecracker/pull/5088): Added + AMD Genoa as a supported and tested platform for Firecracker. + +### Changed + +- [#4913](https://github.com/firecracker-microvm/firecracker/pull/4913): Removed + unnecessary fields (`max_connections` and `max_pending_resets`) from the + snapshot format, bumping the snapshot version to 5.0.0. Users need to + regenerate snapshots. +- [#4926](https://github.com/firecracker-microvm/firecracker/pull/4926): Replace + underlying implementation for seccompiler from in house one in favor of + `libseccomp` which produces smaller and more optimized BPF code. + +### Deprecated + +### Removed + +### Fixed + +- [#4921](https://github.com/firecracker-microvm/firecracker/pull/4921): Fixed + swagger `CpuConfig` definition to include missing aarch64-specific fields. +- [#4916](https://github.com/firecracker-microvm/firecracker/pull/4916): Fixed + `IovDeque` implementation to work with any host page size. This fixes + virtio-net device on non 4K host kernels. +- [#4991](https://github.com/firecracker-microvm/firecracker/pull/4991): Fixed + `mem_size_mib` and `track_dirty_pages` being mandatory for all + `PATCH /machine-config` requests. Now, they can be omitted which leaves these + parts of the machine configuration unchanged. +- [#5007](https://github.com/firecracker-microvm/firecracker/pull/5007): Fixed + watchdog softlockup warning on x86_64 guests when a vCPU is paused during GDB + debugging. +- [#5021](https://github.com/firecracker-microvm/firecracker/pull/5021) If a + balloon device is inflated post UFFD-backed snapshot restore, Firecracker now + causes `remove` UFFD messages to be sent to the UFFD handler. Previously, no + such message would be sent. +- [#5034](https://github.com/firecracker-microvm/firecracker/pull/5034): Fix an + integer underflow in the jailer when computing the value it passes to + Firecracker's `--parent-cpu-time-us` values, which caused development builds + of Firecracker to crash (but production builds were unaffected as underflows + do not panic in release mode). +- [#5045](https://github.com/firecracker-microvm/firecracker/pull/5045): Fixed + an issue where firecracker intermittently receives SIGHUP when using jailer + with `--new-pid-ns` but without `--daemonize`. +- [#4995](https://github.com/firecracker-microvm/firecracker/pull/4995): + Firecracker no longer overwrites CPUID leaf 0x80000000 when running AMD + hardware, meaning the guest can now discover a greater range of CPUID leaves + in the extended function range (this range is host kernel dependent). +- [#5046](https://github.com/firecracker-microvm/firecracker/pull/5046): Retry + KVM_CREATE_VM on EINTR that occasionally happen on heavily loaded hosts to + improve reliability of microVM creation. +- [#5052](https://github.com/firecracker-microvm/firecracker/pull/5052): Build + the empty seccomp policy as default for debug builds to avoid crashes on + syscalls introduced by debug assertions from Rust 1.80.0. + +## [1.10.1] + +### Changed + +- [#4907](https://github.com/firecracker-microvm/firecracker/pull/4907): Bumped + the snapshot version to 4.0.0, so users need to regenerate snapshots. + +## [1.10.0] ### Added @@ -33,10 +147,6 @@ and this project adheres to ### Changed -- [#4875](https://github.com/firecracker-microvm/firecracker/pull/4875): - Increase default queue size for the `virtio-net` device from 256 to 512. This - decreases wait time between guest and vmm threads for network packets - processing and allows for more throughput. - [#4844](https://github.com/firecracker-microvm/firecracker/pull/4844): Upgrade `virtio-net` device to use `readv` syscall to avoid unnecessary memory copies on RX path, increasing the RX performance. @@ -67,7 +177,7 @@ and this project adheres to in the restore path. This was leading to inability to connect to the restored VM if the offload features were used. -## \[1.9.0\] +## [1.9.0] ### Added @@ -75,7 +185,8 @@ and this project adheres to VMGenID support for microVMs running on ARM hosts with 6.1 guest kernels. Support for VMGenID via DeviceTree bindings exists only on mainline 6.10 Linux onwards. Users of Firecracker will need to backport the relevant patches on - top of their 6.1 kernels to make use of the feature. + top of their 6.1 kernels to make use of the feature. As a result, Firecracker + snapshot version is now 3.0.0 - [#4732](https://github.com/firecracker-microvm/firecracker/pull/4732), [#4733](https://github.com/firecracker-microvm/firecracker/pull/4733), [#4741](https://github.com/firecracker-microvm/firecracker/pull/4741), @@ -111,7 +222,7 @@ and this project adheres to different assumptions. This PR fixes the emulation code to set the TAP features based on the features accepted by the guest. -## \[1.8.0\] +## [1.8.0] ### Added @@ -204,7 +315,7 @@ and this project adheres to could lead to them seemingly getting stuck in sleep-related syscalls (see also https://github.com/firecracker-microvm/firecracker/pull/4099). -## \[1.7.0\] +## [1.7.0] ### Added @@ -281,7 +392,7 @@ and this project adheres to content is empty, because the 'Content-Length' header field was missing in a response. -## \[1.6.0\] +## [1.6.0] ### Added @@ -373,7 +484,7 @@ and this project adheres to "write-back" of all memory that was updated since the snapshot was originally loaded. -## \[1.5.0\] +## [1.5.0] ### Added @@ -439,10 +550,10 @@ and this project adheres to - Fixed a change in behavior of normalize host brand string that breaks Firecracker on external instances. -- Fixed the T2A CPU template not to unset the MMX bit - (CPUID.80000001h:EDX\[23\]) and the FXSR bit (CPUID.80000001h:EDX\[24\]). +- Fixed the T2A CPU template not to unset the MMX bit (CPUID.80000001h:EDX[23]) + and the FXSR bit (CPUID.80000001h:EDX[24]). - Fixed the T2A CPU template to set the RstrFpErrPtrs bit - (CPUID.80000008h:EBX\[2\]). + (CPUID.80000008h:EBX[2]). - Fixed a bug where Firecracker would crash during boot if a guest set up a virtio queue that partially overlapped with the MMIO gap. Now Firecracker instead correctly refuses to activate the corresponding virtio device. @@ -461,7 +572,7 @@ and this project adheres to misspelled param ("nomodules") being present at the command line, since this param will no longer be passed. -## \[1.4.0\] +## [1.4.0] ### Added @@ -476,8 +587,8 @@ and this project adheres to ### Changed -- Set FDP_EXCPTN_ONLY bit (CPUID.7h.0:EBX\[6\]) and ZERO_FCS_FDS bit - (CPUID.7h.0:EBX\[13\]) in Intel's CPUID normalization process. +- Set FDP_EXCPTN_ONLY bit (CPUID.7h.0:EBX[6]) and ZERO_FCS_FDS bit + (CPUID.7h.0:EBX[13]) in Intel's CPUID normalization process. ### Fixed @@ -498,10 +609,9 @@ and this project adheres to - Fixed passing through cache information from host in CPUID leaf 0x80000005. - Fixed the T2A CPU template to disable SVM (nested virtualization). - Fixed the T2A CPU template to set EferLmsleUnsupported bit - (CPUID.80000008h:EBX\[20\]), which indicates that EFER\[LMSLE\] is not - supported. + (CPUID.80000008h:EBX[20]), which indicates that EFER[LMSLE] is not supported. -## \[1.3.0\] +## [1.3.0] ### Added @@ -521,7 +631,7 @@ and this project adheres to - Fixed feature flags in T2 CPU template on Intel Ice Lake. -## \[1.2.0\] +## [1.2.0] ### Added @@ -566,7 +676,7 @@ and this project adheres to of Firecracker \<= 1.1.3. - Improved stability and security when saving CPU MSRs in snapshots. -## \[1.1.0\] +## [1.1.0] ### Added @@ -628,7 +738,7 @@ and this project adheres to - Fixed incosistency in the swagger definition with the current state of the `/vm/config` endpoint. -## \[1.0.0\] +## [1.0.0] ### Added @@ -703,7 +813,7 @@ and this project adheres to - Fix jailer's cgroup implementation to accept properties that contain multiple dots. -## \[0.25.0\] +## [0.25.0] ### Added @@ -762,7 +872,7 @@ and this project adheres to Epoch, as the name suggests. It was previously using a monotonic clock with an undefined starting point. -## \[0.24.0\] +## [0.24.0] ### Added @@ -799,7 +909,7 @@ and this project adheres to - Fixed inconsistency in YAML file InstanceInfo definition -## \[0.23.0\] +## [0.23.0] ### Added @@ -850,7 +960,7 @@ and this project adheres to - Changed `devtool build` to build jailer binary for `musl` only targets. Building jailer binary for `non-musl` targets have been removed. -## \[0.22.0\] +## [0.22.0] ### Added @@ -914,7 +1024,7 @@ and this project adheres to - Segregated MMDS documentation in MMDS design documentation and MMDS user guide documentation. -## \[0.21.0\] +## [0.21.0] ### Added @@ -950,7 +1060,7 @@ and this project adheres to functionality is available through the PATCH /drives API. See `docs/api_requests/patch-block.md`. -## \[0.20.0\] +## [0.20.0] ### Added @@ -978,7 +1088,7 @@ and this project adheres to - Decreased release binary size by 10%. -## \[0.19.0\] +## [0.19.0] ### Added @@ -1021,7 +1131,7 @@ and this project adheres to - Changed the vsock property `id` to `vsock_id` so that the API client can be successfully generated from the swagger definition. -## \[0.18.0\] +## [0.18.0] ### Added @@ -1048,7 +1158,7 @@ and this project adheres to - Removed experimental support for vhost-based vsock devices. -## \[0.17.0\] +## [0.17.0] ### Added @@ -1059,7 +1169,7 @@ and this project adheres to - Added a signal handler for `SIGBUS` and `SIGSEGV` that immediately terminates the process upon intercepting the signal. - Added documentation for signal handling utilities. -- Added \[alpha\] aarch64 support. +- Added [alpha] aarch64 support. - Added metrics for successful read and write operations of MMDS, Net and Block devices. @@ -1074,11 +1184,11 @@ and this project adheres to - Incorrect handling of bind mounts within the jailed rootfs. - Corrected the guide for `Alpine` guest setup. -## \[0.16.0\] +## [0.16.0] ### Added -- Added \[alpha\] AMD support. +- Added [alpha] AMD support. - New `devtool` command: `prepare_release`. This updates the Firecracker version, crate dependencies and credits in preparation for a new release. - New `devtool` command: `tag`. This creates a new git tag for the specified @@ -1104,13 +1214,13 @@ and this project adheres to - Removed the `seccomp.bad_syscalls` metric. -## \[0.15.2\] +## [0.15.2] ### Fixed - Corrected the conditional compilation of the seccomp rule for `madvise`. -## \[0.15.1\] +## [0.15.1] ### Fixed @@ -1118,7 +1228,7 @@ and this project adheres to list to prevent Firecracker from terminating abruptly when allocating memory in certain conditions. -## \[0.15.0\] +## [0.15.0] ### Added @@ -1143,7 +1253,7 @@ and this project adheres to - Vsock devices can be attached when starting Firecracker using the jailer. - Vsock devices work properly when seccomp filtering is enabled. -## \[0.14.0\] +## [0.14.0] ### Added @@ -1163,7 +1273,7 @@ and this project adheres to - Fixed build with the `vsock` feature. -## \[0.13.0\] +## [0.13.0] ### Added @@ -1190,7 +1300,7 @@ and this project adheres to - Removed `InstanceHalt` from the list of possible actions. -## \[0.12.0\] +## [0.12.0] ### Added @@ -1220,7 +1330,7 @@ and this project adheres to - Ensure MMDS compatibility with C5's IMDS implementation. - Corrected the swagger specification to ensure `OpenAPI 2.0` compatibility. -## \[0.11.0\] +## [0.11.0] ### Added @@ -1250,13 +1360,13 @@ and this project adheres to - Fixed bug in `PATCH /drives`, whereby the ID in the path was not checked against the ID in the body. -## \[0.10.1\] +## [0.10.1] ### Fixed - The Swagger definition was corrected. -## \[0.10.0\] +## [0.10.0] ### Added @@ -1282,7 +1392,7 @@ and this project adheres to - The microVM ID prefixes each Firecracker log line. This ID also appears in the process `cmdline` so it's now possible to `ps | grep ` for it. -## \[0.9.0\] +## [0.9.0] ### Added @@ -1316,7 +1426,7 @@ and this project adheres to - Remove the `action_id` parameter for `InstanceStart`, both from the URI and the JSON request body. -## \[0.8.0\] +## [0.8.0] ### Added @@ -1331,7 +1441,7 @@ and this project adheres to - Replaced the `permissions` property of `/drives` resources with a boolean. - Removed the `state` property of `/drives` resources. -## \[0.7.0\] +## [0.7.0] ### Added @@ -1356,7 +1466,7 @@ and this project adheres to guest kernel boot. - Fixed network emulation to improve IO performance. -## \[0.6.0\] +## [0.6.0] ### Added @@ -1385,7 +1495,7 @@ and this project adheres to - It is now possible to create more than one network tun/tap interface inside a jailed Firecracker. -## \[0.5.0\] +## [0.5.0] ### Added @@ -1424,7 +1534,7 @@ and this project adheres to - Removed a leftover file generated by the logger unit tests. - Removed `firecracker-v1.0.yaml`. -## \[0.4.0\] +## [0.4.0] ### Added @@ -1475,7 +1585,7 @@ and this project adheres to - Removed `--vmm-no-api` command line option. Firecracker can only be started via the API. -## \[0.3.0\] +## [0.3.0] ### Added @@ -1511,7 +1621,7 @@ and this project adheres to - Removed support for attaching vsock devices. - Removed support for building Firecracker with glibc. -## \[0.2.0\] +## [0.2.0] ### Added @@ -1549,7 +1659,7 @@ and this project adheres to - Removed `api/swagger/firecracker-mvp.yaml`. - Removed `api/swagger/limiters.yaml`. -## \[0.1.1\] +## [0.1.1] ### Changed @@ -1564,7 +1674,7 @@ and this project adheres to - Fixed an issue which caused compilation problems, due to a compatibility breaking transitive dependency in the tokio suite of crates. -## \[0.1.0\] +## [0.1.0] ### Added diff --git a/CREDITS.md b/CREDITS.md index 2610d0e2962..f8e68078dcc 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -36,6 +36,7 @@ Contributors to the Firecracker repository: - Alexandru Cihodaru - Alexandru-Cezar Sardan - Alin Dima +- Anatoli Babenia - Andrea Manzini - Andreea Florescu - Andrei Casu-Pop @@ -73,6 +74,7 @@ Contributors to the Firecracker repository: - Chris Christensen - Christian González - Christopher Diehl +- Christos Katsakioris - cneira - Colin Percival - Colton J. McCurdy @@ -125,6 +127,7 @@ Contributors to the Firecracker repository: - Iulian Barbu - Ives van Hoorne - Jack Thomson +- jackabald - James Curtis - James Turnbull - Javier Romero @@ -142,6 +145,7 @@ Contributors to the Firecracker repository: - Julian Stecklina - Justus Adam - Ján Mochňak +- kanpov - karthik nedunchezhiyan - KarthikVelayutham - Kazuyoshi Kato @@ -156,6 +160,7 @@ Contributors to the Firecracker repository: - Liviu Berciu - Lloyd - lloydmeta +- longxiangqiao - Lorenzo Fontana - LOU Xun - Lucas Zanela @@ -172,6 +177,7 @@ Contributors to the Firecracker repository: - Massimiliano Torromeo - Matias Teragni - Matt Wilson +- Matthew Buckingham-Bishop - Matthew Schlebusch - Max Wittek - Mehrdad Arshad Rad @@ -212,6 +218,7 @@ Contributors to the Firecracker repository: - Ria - Riccardo Mancini - Richard Case +- River Phillips - Rob Devereux - Robert Grimes - Rodrigue Chakode @@ -234,6 +241,7 @@ Contributors to the Firecracker repository: - Sripracha - Stefan Nita <32079871+stefannita01@users.noreply.github.com> - StemCll +- Steven Wirges - Sudan Landge - sundar.preston.789@gmail.com - Takahiro Itazuri @@ -245,6 +253,7 @@ Contributors to the Firecracker repository: - timvisee - Tobias Pfandzelter - Tomas Valenta +- tommady - Tomoya Iwata - Trăistaru Andrei Cristian - Tyler Anton diff --git a/Cargo.lock b/Cargo.lock index ad43f6af62c..79cc44a4567 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,15 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "acpi_tables" version = "0.1.0" dependencies = [ "displaydoc", - "thiserror", + "thiserror 2.0.12", "vm-memory", - "zerocopy 0.8.8", + "zerocopy 0.8.24", ] [[package]] @@ -64,9 +64,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -98,17 +98,18 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.59.0", + "once_cell", + "windows-sys", ] [[package]] @@ -125,46 +126,41 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-lc-fips-sys" -version = "0.12.13" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf12b67bc9c5168f68655aadb2a12081689a58f1d9b1484705e4d1810ed6e4ac" +checksum = "2d9c2e952a1f57e8cbc78b058a968639e70c4ce8b9c0a5e6363d4e5670eed795" dependencies = [ - "bindgen 0.69.4", + "bindgen 0.69.5", "cc", "cmake", "dunce", "fs_extra", - "libc", - "paste", + "regex", ] [[package]] name = "aws-lc-rs" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd82dba44d209fddb11c190e0a94b78651f95299598e472215667417a03ff1d" +checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", - "mirai-annotations", - "paste", "untrusted", "zeroize", ] [[package]] name = "aws-lc-sys" -version = "0.22.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7a4168111d7eb622a31b214057b8509c0a7e1794f44c546d742330dc793972" +checksum = "b9f7720b74ed28ca77f90769a71fd8c637a0137f6fae4ae947e1050229cff57f" dependencies = [ - "bindgen 0.69.4", + "bindgen 0.69.5", "cc", "cmake", "dunce", "fs_extra", - "libc", - "paste", ] [[package]] @@ -175,11 +171,22 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bincode" -version = "1.3.3" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" dependencies = [ + "bincode_derive", "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", ] [[package]] @@ -188,7 +195,7 @@ version = "0.68.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "lazy_static", @@ -204,11 +211,11 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.10.5", @@ -233,9 +240,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "byteorder" @@ -245,9 +252,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cargo_toml" -version = "0.20.5" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88da5a13c620b4ca0078845707ea9c3faf11edbc3ffd8497d11d686211cd1ac0" +checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" dependencies = [ "serde", "toml", @@ -261,9 +268,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.34" +version = "1.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" dependencies = [ "jobserver", "libc", @@ -285,12 +292,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "ciborium" version = "0.2.2" @@ -341,9 +342,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -351,18 +352,18 @@ dependencies = [ [[package]] name = "clap-num" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e063d263364859dc54fb064cedb7c122740cd4733644b14b176c097f51e8ab7" +checksum = "822c4000301ac390e65995c62207501e3ef800a1fc441df913a5e8e4dc374816" dependencies = [ "num-traits", ] [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -372,9 +373,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -384,16 +385,16 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clippy-tracing" version = "0.1.0" dependencies = [ "clap", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", "syn", @@ -403,9 +404,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.51" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] @@ -418,7 +419,7 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "cpu-template-helper" -version = "1.10.0-dev" +version = "1.12.0-dev" dependencies = [ "clap", "displaydoc", @@ -426,16 +427,16 @@ dependencies = [ "log-instrument", "serde", "serde_json", - "thiserror", + "thiserror 2.0.12", "vmm", "vmm-sys-util", ] [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -482,9 +483,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" @@ -507,18 +508,18 @@ dependencies = [ [[package]] name = "derive_more" -version = "1.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "1.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", @@ -551,15 +552,15 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -567,31 +568,31 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", + "jiff", "log", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -606,9 +607,8 @@ dependencies = [ [[package]] name = "firecracker" -version = "1.10.0-dev" +version = "1.12.0-dev" dependencies = [ - "bincode", "cargo_toml", "displaydoc", "event-manager", @@ -620,7 +620,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 2.0.12", "timerfd", "userfaultfd", "utils", @@ -636,11 +636,11 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "gdbstub" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c683a9f13de31432e6097131d5f385898c7f0635c0f392b9d0fa165063c8ac" +checksum = "8062b93565ea9fe2e60a0dd3c252f0d48c27cf223dad7ead028e361181a2c1a5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "log", "managed", @@ -676,7 +676,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", ] [[package]] @@ -691,15 +703,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "half" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" dependencies = [ "cfg-if", "crunchy", @@ -707,9 +719,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -719,30 +731,24 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "indexmap" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", @@ -750,22 +756,22 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -785,32 +791,55 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jailer" -version = "1.10.0-dev" +version = "1.12.0-dev" dependencies = [ "libc", "log-instrument", - "nix 0.29.0", "regex", - "thiserror", + "thiserror 2.0.12", "utils", "vmm-sys-util", ] +[[package]] +name = "jiff" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "jobserver" version = "0.1.32" @@ -822,9 +851,9 @@ dependencies = [ [[package]] name = "kvm-bindings" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4933174d0cc4b77b958578cd45784071cc5ae212c2d78fbd755aaaa6dfa71a" +checksum = "3b13baf7bdfda2e10bcb109fcb099ef40cff82374eb6b7cdcf4695bdec4e522c" dependencies = [ "serde", "vmm-sys-util", @@ -833,11 +862,11 @@ dependencies = [ [[package]] name = "kvm-ioctls" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "337d1afa126368bbd6a5c328048f71a69a737e9afe7e436b392a8f8d770c9171" +checksum = "083c460d5a272c2f22205973e319147b791d92a288d7d7a8d4c6194f95229440" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "kvm-bindings", "libc", "vmm-sys-util", @@ -857,26 +886,20 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets", ] -[[package]] -name = "libm" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" - [[package]] name = "linux-loader" version = "0.13.0" @@ -888,15 +911,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" dependencies = [ "serde", ] @@ -943,7 +966,7 @@ dependencies = [ [[package]] name = "micro_http" version = "0.1.0" -source = "git+https://github.com/firecracker-microvm/micro-http#8182cd5523b63ceb52ad9d0e7eb6fb95683e6d1b" +source = "git+https://github.com/firecracker-microvm/micro-http#e854e50bc06a7e7bc0e5f5835d8f3f951e21f05f" dependencies = [ "libc", "vmm-sys-util", @@ -955,32 +978,14 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - [[package]] name = "nix" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "libc", -] - -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", - "cfg_aliases", "libc", ] @@ -1001,20 +1006,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" -version = "11.1.4" +version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "opaque-debug" @@ -1046,20 +1050,35 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy 0.8.24", ] [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" dependencies = [ "proc-macro2", "syn", @@ -1067,24 +1086,24 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "lazy_static", "num-traits", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax", "unarray", @@ -1092,9 +1111,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -1106,8 +1125,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.0", + "zerocopy 0.8.24", ] [[package]] @@ -1117,7 +1147,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.0", ] [[package]] @@ -1126,7 +1166,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.24", ] [[package]] @@ -1135,17 +1185,17 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] name = "rebase-snap" -version = "1.10.0-dev" +version = "1.12.0-dev" dependencies = [ "displaydoc", "libc", "log-instrument", - "thiserror", + "thiserror 2.0.12", "utils", "vmm-sys-util", ] @@ -1164,9 +1214,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -1187,22 +1237,22 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.38" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -1215,42 +1265,41 @@ dependencies = [ [[package]] name = "seccompiler" -version = "1.10.0-dev" +version = "1.12.0-dev" dependencies = [ "bincode", + "clap", "displaydoc", "libc", - "log-instrument", "serde", "serde_json", - "thiserror", - "utils", - "vmm-sys-util", + "thiserror 2.0.12", + "zerocopy 0.8.24", ] [[package]] name = "semver" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.214" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -1259,9 +1308,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -1295,7 +1344,7 @@ dependencies = [ [[package]] name = "snapshot-editor" -version = "1.10.0-dev" +version = "1.12.0-dev" dependencies = [ "clap", "clap-num", @@ -1303,7 +1352,7 @@ dependencies = [ "libc", "log-instrument", "semver", - "thiserror", + "thiserror 2.0.12", "utils", "vmm", "vmm-sys-util", @@ -1323,9 +1372,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -1334,18 +1383,38 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.67" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3c6efbfc763e64eb85c11c25320f0737cb7364c4b6336db90aa9ebe27a0bbd" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.12", ] [[package]] name = "thiserror-impl" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -1373,9 +1442,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", "serde_spanned", @@ -1394,9 +1463,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "serde", @@ -1407,9 +1476,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unarray" @@ -1419,9 +1488,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-xid" @@ -1445,17 +1514,23 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "userfaultfd" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18d8b176d4d3e420685e964f87c25df5fdd5b26d7eb0d0e7c892d771f5b81035" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "libc", - "nix 0.27.1", - "thiserror", + "nix", + "thiserror 1.0.69", "userfaultfd-sys", ] @@ -1480,33 +1555,28 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" name = "utils" version = "0.1.0" dependencies = [ - "derive_more", "displaydoc", "libc", "log-instrument", - "serde", - "serde_json", - "thiserror", - "vm-memory", - "vmm-sys-util", + "thiserror 2.0.12", ] [[package]] name = "uuid" -version = "1.11.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom", - "rand", + "getrandom 0.3.1", + "rand 0.9.0", "uuid-macro-internal", ] [[package]] name = "uuid-macro-internal" -version = "1.11.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b91f57fe13a38d0ce9e28a03463d8d3c2468ed03d75375110ec71d93b449a08" +checksum = "72dcd78c4f979627a754f5522cea6e6a25e55139056535fe6e69c506cd64a862" dependencies = [ "proc-macro2", "quote", @@ -1525,13 +1595,19 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bce0aad4d8776cb64f1ac591e908a561c50ba6adac4416296efee590b155623f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "libc", "uuid", "vm-memory", "vmm-sys-util", ] +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "vm-allocator" version = "0.1.1" @@ -1539,7 +1615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e4ce718bd4e8d74b1747363e27f715a6b1bd6971597cb21425dadbf4e712241" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1550,12 +1626,12 @@ checksum = "7e21282841a059bb62627ce8441c491f09603622cd5a21c43bfedc85a2952f23" [[package]] name = "vm-memory" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2919f87420b6998a131eb7c78843890295e91a3f8f786ccc925c8d387b75121" +checksum = "f1720e7240cdc739f935456eb77f370d7e9b2a3909204da1e2b47bef1137a013" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", "winapi", ] @@ -1575,7 +1651,7 @@ dependencies = [ "aws-lc-rs", "base64", "bincode", - "bitflags 2.6.0", + "bitflags 2.9.0", "crc64", "criterion", "derive_more", @@ -1584,10 +1660,9 @@ dependencies = [ "event-manager", "gdbstub", "gdbstub_arch", - "itertools 0.13.0", + "itertools 0.14.0", "kvm-bindings", "kvm-ioctls", - "lazy_static", "libc", "linux-loader", "log", @@ -1595,12 +1670,11 @@ dependencies = [ "memfd", "micro_http", "proptest", - "seccompiler", "semver", "serde", "serde_json", "slab", - "thiserror", + "thiserror 2.0.12", "timerfd", "userfaultfd", "utils", @@ -1610,7 +1684,7 @@ dependencies = [ "vm-memory", "vm-superio", "vmm-sys-util", - "zerocopy 0.8.8", + "zerocopy 0.8.24", ] [[package]] @@ -1641,6 +1715,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "which" version = "4.4.2" @@ -1675,7 +1758,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -1684,15 +1767,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.59.0" @@ -1768,13 +1842,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -1787,11 +1870,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.8" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a4e33e6dce36f2adba29746927f8e848ba70989fdb61c772773bbdda8b5d6a7" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ - "zerocopy-derive 0.8.8", + "zerocopy-derive 0.8.24", ] [[package]] @@ -1807,9 +1890,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.8" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd137b4cc21bde6ecce3bbbb3350130872cda0be2c6888874279ea76e17d4c1" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 914c4cdc87c..4f8cc3f5eb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ resolver = "2" [workspace.lints.rust] missing_debug_implementations = "warn" +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } [workspace.lints.clippy] ptr_as_ptr = "warn" diff --git a/DEPRECATED.md b/DEPRECATED.md index ca9068d773d..e20e4af628a 100644 --- a/DEPRECATED.md +++ b/DEPRECATED.md @@ -19,3 +19,5 @@ a future major Firecracker release, in accordance with our - \[[#4428](https://github.com/firecracker-microvm/firecracker/pull/4428)\] Booting microVMs using MPTable and command line parameters for VirtIO devices. The functionality is substituted with ACPI. +- \[[#2628](https://github.com/firecracker-microvm/firecracker/pull/2628)\] The + `--basic` parameter of `seccompiler-bin`. diff --git a/FAQ.md b/FAQ.md index 6697ac9f4d1..3ace710fc96 100644 --- a/FAQ.md +++ b/FAQ.md @@ -60,11 +60,10 @@ minimal required device model to the guest operating system while excluding non-essential functionality (only 6 emulated devices are available: virtio-net, virtio-balloon, virtio-block, virtio-vsock, serial console, and a minimal keyboard controller used only to stop the microVM). This, along with a -streamlined kernel loading process enables a \< 125 ms startup time and a \< 5 -MiB memory footprint. The Firecracker process also provides a RESTful control -API, handles resource rate limiting for microVMs, and provides a microVM -metadata service to enable the sharing of configuration data between the host -and guest. +streamlined kernel loading process enables a < 125 ms startup time and a < 5 MiB +memory footprint. The Firecracker process also provides a RESTful control API, +handles resource rate limiting for microVMs, and provides a microVM metadata +service to enable the sharing of configuration data between the host and guest. ### What operating systems are supported by Firecracker? @@ -197,9 +196,9 @@ mapping: ### How can I gracefully reboot the guest? How can I gracefully poweroff the guest? -Firecracker does not implement ACPI and PM devices, therefore operations like -gracefully rebooting or powering off the guest are supported in unconventional -ways. +Firecracker does not virtualize guest power management, therefore operations +like gracefully rebooting or powering off the guest are supported in +unconventional ways. Running the `poweroff` or `halt` commands inside a Linux guest will bring it down but Firecracker process remains unaware of the guest shutdown so it lives diff --git a/NOTICE b/NOTICE index ff113a0f1a4..f2daaa7be8d 100644 --- a/NOTICE +++ b/NOTICE @@ -5,3 +5,8 @@ SPDX-License-Identifier: Apache-2.0 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 THIRD-PARTY file. + +The Firecracker release bundle includes libseccomp which is available +under the LGPLv2.1 license. This is used in the Firecracker build process +to produce cBPF bytecode that is shipped alongside Firecracker for use by +the Linux kernel. diff --git a/README.md b/README.md index dde5c21e794..dcb5771f994 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ The **API endpoint** can be used to: - Add a [entropy device](docs/entropy.md) to the microVM. - Start the microVM using a given kernel image, root file system, and boot arguments. -- \[x86_64 only\] Stop the microVM. +- [x86_64 only] Stop the microVM. **Built-in Capabilities**: @@ -130,14 +130,15 @@ The **API endpoint** can be used to: We test all combinations of: -| Instance | Host OS & Kernel | Guest Rootfs | Guest Kernel | -| :-------- | :---------------- | :----------- | :----------- | -| c5n.metal | al2 linux_5.10 | ubuntu 22.04 | linux_5.10 | -| m5n.metal | al2023 linux_6.1 | | linux_6.1 | -| m6i.metal | | | | -| m6a.metal | | | | -| m6g.metal | | | | -| m7g.metal | | | | +| Instance | Host OS & Kernel | Guest Rootfs | Guest Kernel | +| :------------- | :--------------- | :----------- | :----------- | +| c5n.metal | al2 linux_5.10 | ubuntu 24.04 | linux_5.10 | +| m5n.metal | al2023 linux_6.1 | | linux_6.1 | +| m6i.metal | | | | +| m6a.metal | | | | +| m7a.metal-48xl | | | | +| m6g.metal | | | | +| m7g.metal | | | | ## Known issues and Limitations diff --git a/deny.toml b/deny.toml index 5cd62715860..be3a1040463 100644 --- a/deny.toml +++ b/deny.toml @@ -5,7 +5,7 @@ allow = [ "Apache-2.0", "BSD-3-Clause", "ISC", - "Unicode-DFS-2016", + "Unicode-3.0", "OpenSSL" ] diff --git a/docs/RELEASE_POLICY.md b/docs/RELEASE_POLICY.md index fb95c55e402..c9bd2a51089 100644 --- a/docs/RELEASE_POLICY.md +++ b/docs/RELEASE_POLICY.md @@ -90,9 +90,10 @@ v3.1 will be patched since were the last two Firecracker releases and less than | Release | Release Date | Latest Patch | Min. end of support | Official end of Support | | ------: | -----------: | -----------: | ------------------: | :------------------------------ | -| v1.10 | 2024-11-07 | v1.10.0 | 2025-05-07 | Supported | -| v1.9 | 2024-09-02 | v1.9.1 | 2025-03-02 | Supported | -| v1.8 | 2024-07-10 | v1.8.0 | 2025-01-10 | Supported | +| v1.11 | 2025-03-18 | v1.11.0 | 2025-09-18 | Supported | +| v1.10 | 2024-11-07 | v1.10.1 | 2025-05-07 | Supported | +| v1.9 | 2024-09-02 | v1.9.1 | 2025-03-02 | 2025-03-18 (v1.11 released) | +| v1.8 | 2024-07-10 | v1.8.0 | 2025-01-10 | 2025-01-10 (end of 6mo support) | | v1.7 | 2024-03-18 | v1.7.0 | 2024-09-18 | 2024-09-18 (end of 6mo support) | | v1.6 | 2023-12-20 | v1.6.0 | 2024-06-20 | 2024-07-10 (v1.8 released) | | v1.5 | 2023-10-09 | v1.5.1 | 2024-04-09 | 2024-04-09 (end of 6mo support) | diff --git a/docs/api_requests/actions.md b/docs/api_requests/actions.md index c72f12372dc..40700c359de 100644 --- a/docs/api_requests/actions.md +++ b/docs/api_requests/actions.md @@ -31,7 +31,7 @@ curl --unix-socket /tmp/firecracker.socket -i \ -d '{ "action_type": "FlushMetrics" }' ``` -## \[Intel and AMD only\] SendCtrlAltDel +## [Intel and AMD only] SendCtrlAltDel This action will send the CTRL+ALT+DEL key sequence to the microVM. By convention, this sequence has been used to trigger a soft reboot and, as such, diff --git a/docs/api_requests/block-io-engine.md b/docs/api_requests/block-io-engine.md index 8d8521bca3b..733aa374acd 100644 --- a/docs/api_requests/block-io-engine.md +++ b/docs/api_requests/block-io-engine.md @@ -6,7 +6,7 @@ system calls. Firecracker 1.0.0 adds support for an asynchronous block device IO engine. -> \[!WARNING\] +> [!WARNING] > > Support is currently in **developer preview**. See > [this section](#developer-preview-status) for more info. diff --git a/docs/api_requests/block-vhost-user.md b/docs/api_requests/block-vhost-user.md index 8325992caf4..4a3a4dee918 100644 --- a/docs/api_requests/block-vhost-user.md +++ b/docs/api_requests/block-vhost-user.md @@ -1,6 +1,6 @@ # Vhost-user block device -> \[!WARNING\] +> [!WARNING] > > Support is currently in **developer preview**. See > [this section](../RELEASE_POLICY.md#developer-preview-features) for more info. diff --git a/docs/ballooning.md b/docs/ballooning.md index f5a19271265..f2953d630b5 100644 --- a/docs/ballooning.md +++ b/docs/ballooning.md @@ -263,3 +263,20 @@ cannot be enabled later by providing a `polling_interval` non-zero value. Furthermore, if the balloon was configured with statistics pre-boot through a non-zero `stats_polling_interval_s` value, the statistics cannot be disabled through a `polling_interval` value of zero post-boot. + +## Balloon Caveats + +- Firecracker has no control over the speed of inflation or deflation; this is + dictated by the guest kernel driver. + +- The balloon will continually attempt to reach its target size, which can be a + CPU-intensive process. It is therefore recommended to set realistic targets + or, after a period of stagnation in the inflation, update the target size to + be close to the inflated size. + +- The `deflate_on_oom` flag is a mechanism to prevent the guest from crashing or + terminating processes; it is not meant to be used continually to free memory. + Doing this will be a CPU-intensive process, as the balloon driver is designed + to deflate and release memory slowly. This is also compounded if the balloon + has yet to reach its target size, as it will attempt to inflate while also + deflating. diff --git a/docs/cpu_templates/cpu-templates.md b/docs/cpu_templates/cpu-templates.md index e00e71f26e2..a2d8b06fceb 100644 --- a/docs/cpu_templates/cpu-templates.md +++ b/docs/cpu_templates/cpu-templates.md @@ -180,7 +180,7 @@ curl --unix-socket /tmp/firecracker.socket -i \ -H 'Content-Type: application/json' \ -d '{ "kvm_capabilities": ["171", "172"], - "vcpu_features": [{ "index": 0, "bitmap": "0b1100000" }] + "vcpu_features": [{ "index": 0, "bitmap": "0b11xxxxx" }] "reg_modifiers": [ { "addr": "0x603000000013c020", diff --git a/docs/cpu_templates/cpuid-normalization.md b/docs/cpu_templates/cpuid-normalization.md index 2d7b3f40c81..a42132b6a07 100644 --- a/docs/cpu_templates/cpuid-normalization.md +++ b/docs/cpu_templates/cpuid-normalization.md @@ -21,34 +21,33 @@ See also: [boot protocol settings](boot-protocol.md) | Enable TSC_DEADLINE | 0x1 | - | ECX | 24 | | Enable HYPERVISOR | 0x1 | - | ECX | 31 | | Set HTT value if the microVM's CPU count is greater than 1 | 0x1 | - | EDX | 28 | -| Insert leaf 0xb, subleaf 0x1 filled with `0` if it is not already present | 0xb | 0x1 | all | all | -| Update extended topology enumeration | 0xb | all | EAX | 4:0 | -| Update extended topology enumeration | 0xb | all | EBX | 15:0 | -| Update extended topology enumeration | 0xb | all | ECX | 15:8 | +| Insert leaf 0xb, subleaf 0x1 if not present | 0xb | 0x1 | all | all | +| Fill extended topology enumeration leaf | 0xb | all | all | all | | Pass through L1 cache and TLB information from host | 0x80000005 | - | all | all | | Pass through L2 cache and TLB and L3 cache information from host | 0x80000006 | - | all | all | ## Intel-specific CPUID normalization -| Description | Leaf | Subleaf | Register | Bits | -| -------------------------------------------------------------- | :--------------------------------: | :-----: | :----------------: | :---: | -| Update deterministic cache parameters | 0x4 | all | EAX | 31:14 | -| Disable Intel Turbo Boost technology | 0x6 | - | EAX | 1 | -| Disable frequency selection | 0x6 | - | ECX | 3 | -| Set FDP_EXCPTN_ONLY bit | 0x7 | 0x0 | EBX | 6 | -| Set "Deprecates FPU CS and FPU DS values" bit | 0x7 | 0x0 | EBX | 13 | -| Disable performance monitoring | 0xa | - | EAX, EBX, ECX, EDX | all | -| Update brand string to use a default format and real frequency | 0x80000002, 0x80000003, 0x80000004 | - | EAX, EBX, ECX, EDX | all | +| Description | Leaf | Subleaf | Register | Bits | +| -------------------------------------------------------------- | :--------------------------------: | :-----: | :------: | :---: | +| Update deterministic cache parameters | 0x4 | all | EAX | 31:14 | +| Disable Intel Turbo Boost technology | 0x6 | - | EAX | 1 | +| Disable frequency selection | 0x6 | - | ECX | 3 | +| Set FDP_EXCPTN_ONLY bit | 0x7 | 0x0 | EBX | 6 | +| Set "Deprecates FPU CS and FPU DS values" bit | 0x7 | 0x0 | EBX | 13 | +| Disable WAITPKG (UMONITOR / UMWAIT / TPAUSE) | 0x7 | 0x0 | ECX | 5 | +| Disable performance monitoring | 0xa | - | all | all | +| Fill v2 extended topology enumeration leaf | 0x1f | all | all | all | +| Update brand string to use a default format and real frequency | 0x80000002, 0x80000003, 0x80000004 | - | all | all | ## AMD-specifc CPUID normalization -| Description | Leaf | Subleaf | Register | Bits | -| ---------------------------------------------------- | :--------------------------------: | :-----: | :----------------: | :---: | -| Set IA32_ARCH_CAPABILITIES MSR as not present | 0x7 | - | EDX | 29 | -| Update largest extended function entry to 0x8000001f | 0x80000000 | - | EAX | 31:0 | -| Set topology extension bit | 0x80000001 | - | ECX | 22 | -| Update brand string with a default AMD value | 0x80000002, 0x80000003, 0x80000004 | - | EAX, EBX, ECX, EDX | all | -| Update number of physical threads | 0x80000008 | - | ECX | 7:0 | -| Update APIC ID size | 0x80000008 | - | ECX | 15:12 | -| Update cache topology information | 0x8000001d | all | all | all | -| Update extended APIC ID | 0x8000001e | - | EAX, EBX, ECX | all | +| Description | Leaf | Subleaf | Register | Bits | +| --------------------------------------------- | :--------------------------------: | :-----: | :----------------: | :---: | +| Set IA32_ARCH_CAPABILITIES MSR as not present | 0x7 | - | EDX | 29 | +| Set topology extension bit | 0x80000001 | - | ECX | 22 | +| Update brand string with a default AMD value | 0x80000002, 0x80000003, 0x80000004 | - | EAX, EBX, ECX, EDX | all | +| Update number of physical threads | 0x80000008 | - | ECX | 7:0 | +| Update APIC ID size | 0x80000008 | - | ECX | 15:12 | +| Update cache topology information | 0x8000001d | all | all | all | +| Update extended APIC ID | 0x8000001e | - | EAX, EBX, ECX | all | diff --git a/docs/cpu_templates/schema.json b/docs/cpu_templates/schema.json index 76a11697e06..27844756941 100644 --- a/docs/cpu_templates/schema.json +++ b/docs/cpu_templates/schema.json @@ -26,7 +26,7 @@ "bitmap": { "description": "Bitmap for modifying the 32 bit field in kvm_vcpu_init::features. Must be in the format `0b[01x]{1,32}`. Corresponding bits will be cleared (`0`), set (`1`) or left intact (`x`). (`_`) can be used as a separator.", "type": "string", - "examples": ["0b1100000"] + "examples": ["0b11xxxxx"] } } } diff --git a/docs/formal-verification.md b/docs/formal-verification.md index 29ef16636dd..6a0c2d07574 100644 --- a/docs/formal-verification.md +++ b/docs/formal-verification.md @@ -143,45 +143,40 @@ Verification Time: 0.19135727s ## FAQ **Q:** What is the Kani verifier?\ -**A:** The -[Kani Rust Verifier](https://github.com/model-checking/kani) is a bit-precise -model checker for Rust. Kani is particularly useful for verifying unsafe code -blocks in Rust, where the +**A:** The [Kani Rust Verifier](https://github.com/model-checking/kani) is a +bit-precise model checker for Rust. Kani is particularly useful for verifying +unsafe code blocks in Rust, where the “[unsafe superpowers](https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#unsafe-superpowers)" are unchecked by the compiler. **Q:** What safety properties does Kani verify?\ -**A:** Kani verifies memory -safety properties (e.g., invalid-pointer dereferences, out-of-bounds array -access), user-specified assertions (i.e., `assert!(...)`), the absence of -`panic!()`s (e.g., `unwrap()` on `None` values), and the absence of some types -of unexpected behavior (e.g., arithmetic overflows). For a full overview, see -the +**A:** Kani verifies memory safety properties (e.g., invalid-pointer +dereferences, out-of-bounds array access), user-specified assertions (i.e., +`assert!(...)`), the absence of `panic!()`s (e.g., `unwrap()` on `None` values), +and the absence of some types of unexpected behavior (e.g., arithmetic +overflows). For a full overview, see the [Kani documentation](https://model-checking.github.io/kani/tutorial-kinds-of-failure.html). **Q:** Do we expect all contributors to write harnesses for newly introduced code?\ -**A:** No. Kani is complementary to unit testing, and we do not have -target for “proof coverage”. We employ formal verification in especially -critical code areas. Generally we do not expect someone who might not be -familiar with formal tools to contribute harnesses. We do expect all contributed -code to pass verification though, just like we expect it to pass unit test! +**A:** No. Kani is complementary to unit testing, and we do not have target for +“proof coverage”. We employ formal verification in especially critical code +areas. Generally we do not expect someone who might not be familiar with formal +tools to contribute harnesses. We do expect all contributed code to pass +verification though, just like we expect it to pass unit test! **Q:** How should I report issues related to any Firecracker harnesses?\ -**A:** -Our Kani harnesses verify safety critical invariants. If you discover a flaw in -a harness, please report it using the +**A:** Our Kani harnesses verify safety critical invariants. If you discover a +flaw in a harness, please report it using the [security issue disclosure process](https://github.com/firecracker-microvm/firecracker/blob/main/SECURITY.md). -**Q:** How do I know which properties I should prove in the Kani -harness?\ -**A:** Generally, these are given by some sort of specification. This -can either be the function contract described in its document (e.g. what -relation between input and output do callers expect?), or even something formal -such as the TCP/IP standard. Don't forget to mention the specification in your -proof harness! +**Q:** How do I know which properties I should prove in the Kani harness?\ +**A:** Generally, these are given by some sort of specification. This can either +be the function contract described in its document (e.g. what relation between +input and output do callers expect?), or even something formal such as the +TCP/IP standard. Don't forget to mention the specification in your proof +harness! **Q:** Where do I debug a broken proof?\ -**A:** Check out the Kani book section -on +**A:** Check out the Kani book section on [debugging verification failures](https://model-checking.github.io/kani/debugging-verification-failures.html). diff --git a/docs/getting-started.md b/docs/getting-started.md index cb9a7fee9ee..04077292946 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -9,7 +9,7 @@ You can check if your system meets the requirements by running `firecracker/tools/devtool checkenv`. An opinionated way to run Firecracker is to launch an -[EC2](https://aws.amazon.com/ec2/) `c5.metal` instance with Ubuntu 22.04. +[EC2](https://aws.amazon.com/ec2/) `c5.metal` instance with Ubuntu 24.04. Firecracker requires [the KVM Linux kernel module](https://www.linux-kvm.org/) to perform its virtualization and emulation tasks. @@ -94,25 +94,44 @@ For simplicity, this guide will not use the [`jailer`](../src/jailer/). ### Getting a rootfs and Guest Kernel Image To successfully start a microVM, you will need an uncompressed Linux kernel -binary, and an ext4 file system image (to use as rootfs). This guide uses a 5.10 -kernel image with a Ubuntu 22.04 rootfs from our CI: +binary, and an ext4 file system image (to use as rootfs). This guide uses the +latest kernel image and Ubuntu rootfs available in our CI for the latest +release. ```bash ARCH="$(uname -m)" - -latest=$(wget "http://spec.ccfc.min.s3.amazonaws.com/?prefix=firecracker-ci/v1.10/x86_64/vmlinux-5.10&list-type=2" -O - 2>/dev/null | grep "(?<=)(firecracker-ci/v1.10/x86_64/vmlinux-5\.10\.[0-9]{3})(?=)" -o -P) +release_url="https://github.com/firecracker-microvm/firecracker/releases" +latest_version=$(basename $(curl -fsSLI -o /dev/null -w %{url_effective} ${release_url}/latest)) +CI_VERSION=${latest_version%.*} +latest_kernel_key=$(curl "http://spec.ccfc.min.s3.amazonaws.com/?prefix=firecracker-ci/$CI_VERSION/$ARCH/vmlinux-&list-type=2" \ + | grep -oP "(?<=)(firecracker-ci/$CI_VERSION/$ARCH/vmlinux-[0-9]+\.[0-9]+\.[0-9]{1,3})(?=)" \ + | sort -V | tail -1) # Download a linux kernel binary -wget "https://s3.amazonaws.com/spec.ccfc.min/${latest}" - -# Download a rootfs -wget "https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.10/${ARCH}/ubuntu-22.04.ext4" +wget "https://s3.amazonaws.com/spec.ccfc.min/${latest_kernel_key}" -# Download the ssh key for the rootfs -wget "https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.10/${ARCH}/ubuntu-22.04.id_rsa" +latest_ubuntu_key=$(curl "http://spec.ccfc.min.s3.amazonaws.com/?prefix=firecracker-ci/$CI_VERSION/$ARCH/ubuntu-&list-type=2" \ + | grep -oP "(?<=)(firecracker-ci/$CI_VERSION/$ARCH/ubuntu-[0-9]+\.[0-9]+\.squashfs)(?=)" \ + | sort -V | tail -1) +ubuntu_version=$(basename $latest_ubuntu_key .sqashfs | grep -oE '[0-9]+\.[0-9]+') -# Set user read permission on the ssh key -chmod 400 ./ubuntu-22.04.id_rsa +# Download a rootfs +wget -O ubuntu-$ubuntu_version.squashfs.upstream "https://s3.amazonaws.com/spec.ccfc.min/$latest_ubuntu_key" + +# Create an ssh key for the rootfs +unsquashfs ubuntu-$ubuntu_version.squashfs.upstream +ssh-keygen -f id_rsa -N "" +cp -v id_rsa.pub squashfs-root/root/.ssh/authorized_keys +mv -v id_rsa ./ubuntu-$ubuntu_version.id_rsa +# create ext4 filesystem image +sudo chown -R root:root squashfs-root +truncate -s 400M ubuntu-$ubuntu_version.ext4 +sudo mkfs.ext4 -d squashfs-root -F ubuntu-$ubuntu_version.ext4 + +# Verify everything was correctly set up and print versions +echo "Kernel: $(ls vmlinux-* | tail -1)" +echo "Rootfs: $(ls *.ext4 | tail -1)" +echo "SSH Key: $(ls *.id_rsa | tail -1)" ``` ### Getting a Firecracker Binary @@ -193,17 +212,16 @@ sudo ip link set dev "$TAP_DEV" up # Enable ip forwarding sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward" +sudo iptables -P FORWARD ACCEPT -HOST_IFACE="eth0" +# This tries to determine the name of the host network interface to forward +# VM's outbound network traffic through. If outbound traffic doesn't work, +# double check this returns the correct interface! +HOST_IFACE=$(ip -j route list default |jq -r '.[0].dev') # Set up microVM internet access sudo iptables -t nat -D POSTROUTING -o "$HOST_IFACE" -j MASQUERADE || true -sudo iptables -D FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT \ - || true -sudo iptables -D FORWARD -i "$TAP_DEV" -o "$HOST_IFACE" -j ACCEPT || true sudo iptables -t nat -A POSTROUTING -o "$HOST_IFACE" -j MASQUERADE -sudo iptables -I FORWARD 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -sudo iptables -I FORWARD 1 -i "$TAP_DEV" -o "$HOST_IFACE" -j ACCEPT API_SOCKET="/tmp/firecracker.socket" LOGFILE="./firecracker.log" @@ -238,7 +256,7 @@ sudo curl -X PUT --unix-socket "${API_SOCKET}" \ }" \ "http://localhost/boot-source" -ROOTFS="./ubuntu-22.04.ext4" +ROOTFS="./$(ls *.ext4 | tail -1)" # Set rootfs sudo curl -X PUT --unix-socket "${API_SOCKET}" \ @@ -279,14 +297,16 @@ sudo curl -X PUT --unix-socket "${API_SOCKET}" \ # started before we attempt to SSH into it. sleep 2s +KEY_NAME=./$(ls *.id_rsa | tail -1) + # Setup internet access in the guest -ssh -i ./ubuntu-22.04.id_rsa root@172.16.0.2 "ip route add default via 172.16.0.1 dev eth0" +ssh -i $KEY_NAME root@172.16.0.2 "ip route add default via 172.16.0.1 dev eth0" # Setup DNS resolution in the guest -ssh -i ./ubuntu-22.04.id_rsa root@172.16.0.2 "echo 'nameserver 8.8.8.8' > /etc/resolv.conf" +ssh -i $KEY_NAME root@172.16.0.2 "echo 'nameserver 8.8.8.8' > /etc/resolv.conf" # SSH into the microVM -ssh -i ./ubuntu-22.04.id_rsa root@172.16.0.2 +ssh -i $KEY_NAME root@172.16.0.2 # Use `root` for both the login and password. # Run `reboot` to exit. diff --git a/docs/hugepages.md b/docs/hugepages.md index 0b4b994e04f..a6f18e06dd2 100644 --- a/docs/hugepages.md +++ b/docs/hugepages.md @@ -1,10 +1,5 @@ # Backing Guest Memory by Huge Pages -> \[!WARNING\] -> -> Support is currently in **developer preview**. See -> [this section](RELEASE_POLICY.md#developer-preview-features) for more info. - Firecracker supports backing the guest memory of a VM by 2MB hugetlbfs pages. This can be enabled by setting the `huge_pages` field of `PUT` or `PATCH` requests to the `/machine-config` endpoint to `2M`. @@ -44,7 +39,6 @@ Currently, hugetlbfs support is mutually exclusive with the following Firecracker features: - Memory Ballooning via the [Balloon Device](./ballooning.md) -- Initrd ## FAQ diff --git a/docs/kernel-policy.md b/docs/kernel-policy.md index 630b5fa1040..5f079f0d04b 100644 --- a/docs/kernel-policy.md +++ b/docs/kernel-policy.md @@ -7,31 +7,49 @@ related changes. We are continuously validating the currently supported Firecracker releases (as per [Firecracker’s release policy](../docs/RELEASE_POLICY.md)) using a -combination of: - -- host linux kernel versions 5.10, and 6.1; -- guest linux kernel versions 5.10 and 6.1. Guest linux kernels 4.14 are - deprecated with Firecracker v1.9 and we will drop support for them with - Firecracker v1.10. +combination of all supported host and guest kernel versions in the table below. While other versions and other kernel configs might work, they are not periodically validated in our test suite, and using them might result in unexpected behaviour. Starting with release `v1.0` each major and minor release will specify the supported kernel versions. -Once a kernel version is officially enabled, it is supported for a **minimum of -2 years**. Adding support for a new kernel version will result in a Firecracker -release only if compatibility changes are required. +Once a kernel version is officially added, it is supported for a **minimum of 2 +years**. At least 2 major guest and host versions will be supported at any time. +When support is added for a third kernel version, the oldest will be deprecated +and removed in a following release, after its minimum end of support date. + +### Host Kernel + +| Host kernel | Min. version | Min. end of support | +| ----------: | -----------: | ------------------: | +| v5.10 | v1.0.0 | 2024-01-31 | +| v6.1 | v1.5.0 | 2025-10-12 | -| Host kernel | Guest kernel v4.14 (deprecated) | Guest kernel v5.10 | Guest kernel v6.1 | Min. end of support | -| ----------: | :-----------------------------: | :----------------: | :---------------: | ------------------: | -| v5.10 | Y (deprecated) | Y | Y | 2024-01-31 | -| v6.1 | Y (deprecated) | Y | Y | 2025-10-12 | +### Guest Kernel + +| Guest kernel | Min. version | Min. end of support | +| -----------: | -----------: | ------------------: | +| v5.10 | v1.0.0 | 2024-01-31 | +| v6.1 | v1.9.0 | 2026-09-02 | The guest kernel configs used in our validation pipelines can be found [here](../resources/guest_configs/) while a breakdown of the relevant guest kernel modules can be found in the next section. +We use these configurations to build microVM-specific kernels vended by Amazon +Linux. microVM kernel source code is published in the Amazon Linux +[linux repo](https://github.com/amazonlinux/linux) under tags in the form of +`microvm-kernel-*`, e.g. 6.1.128-3.201.amazn2023 kernel can be found +[here](https://github.com/amazonlinux/linux/tree/microvm-kernel-6.1.128-3.201.amzn2023). +These kernels may have diverged from the equivalent mainline versions, as we +often backport patches that we require for supporting Firecracker features not +present in the kernel versions we officially support. As a result, kernel +configurations found in this repo should be used to build exclusively the +aforementioned Amazon Linux kernels. We do not guarantee that using these +configurations to build upstream kernels, will work or produce usable kernel +images. + ## Guest kernel configuration items The configuration items that may be relevant for Firecracker are: diff --git a/docs/network-setup.md b/docs/network-setup.md index ad77bd3e7e9..fb2c1ca0977 100644 --- a/docs/network-setup.md +++ b/docs/network-setup.md @@ -1,49 +1,108 @@ # Getting Started Firecracker Network Setup -This is a very simple quick-start guide to getting a Firecracker guest connected -to the network. If you're using Firecracker in production, or even want to run -multiple guests, you'll need to adapt this setup. +This is a simple quick-start guide to getting one or more Firecracker microVMs +connected to the Internet via the host. If you run a production setup, you +should consider modifying this setup to accommodate your specific needs. -**Note** Currently firecracker supports only TUN/TAP network backend with no +**Note:** Currently, Firecracker supports only a TUN/TAP network backend with no multi queue support. -The simple steps in this guide assume that your internet-facing interface is -`eth0`, you have nothing else using `tap0` and no other `iptables` rules. Check -out the *Advanced:* sections if that doesn't work for you. +The steps in this guide assume `eth0` to be your Internet-facing network +interface on the host. If `eth0` isn't your main network interface, you should +change the value to the correct one in the commands below. IPv4 is also assumed +to be used, so you will need to adapt the instructions accordingly to support +IPv6. + +Each microVM requires a host network interface (like `eth0`) and a Linux `tap` +device (like `tap0`) used by Firecracker, but the differences in configuration +stem from routing: how packets from the `tap` get to the network interface +(egress) and vice-versa (ingress). There are three main approaches of how to +configure routing for a microVM. + +1. **NAT-based**, which is presented in the main part of this guide. It is + simple but doesn't expose your microVM to the local network (LAN). +1. **Bridge-based**, which exposes your microVM to the local network. Learn more + about in the _Advanced: Bridge-based routing_ section of this guide. +1. **Namespaced NAT**, which sacrifices performance in comparison to the other + approaches but is desired in the scenario when two clones of the same microVM + are running at the same time. To learn more about it, check out the + [Network Connectivity for Clones](./snapshotting/network-for-clones.md) + guide. + +To run multiple microVMs while using NAT-based routing, check out the _Advanced: +Multiple guests_ section. The same principles can be applied to other routing +methods with a bit more effort. + +For the choice of firewall, `nft` is recommended for use on production Linux +systems, but, for the sake of compatibility, this guide provides a choice +between either `nft` or the `iptables-nft` translation layer. The latter is +[no longer recommended](https://access.redhat.com/solutions/6739041) but may be +more familiar to readers. + +## On the Host + +The first step on the host for any microVM is to create a Linux `tap` device, +which Firecracker will use for networking. + +For this setup, only two IP addresses will be necessary - one for the `tap` +device and one for the guest itself, through which you will, for example, `ssh` +into the guest. So, we'll choose the smallest IPv4 subnet needed for 2 +addresses: `/30`. For this VM, let's use the `172.16.0.1` `tap` IP and the +`172.16.0.2` guest IP. -## On The Host +```bash +# Create the tap device. +sudo ip tuntap add tap0 mode tap +# Assign it the tap IP and start up the device. +sudo ip addr add 172.16.0.1/30 dev tap0 +sudo ip link set tap0 up +``` -The first step on the host is to create a `tap` device: +**Note:** The IP of the TAP device should be chosen such that it's not in the +same subnet as the IP address of the host. + +We'll need to enable IPv4 forwarding on the system. ```bash -sudo ip tuntap add tap0 mode tap +echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward ``` -Then you have a few options for routing traffic out of the tap device, through -your host's network interface. One option is NAT, set up like this: +### Configuration via `nft` + +We'll need an nftables table for our routing needs, and 2 chains inside that +table: one for NAT on `postrouting` stage, and another one for filtering on +`forward` stage: ```bash -sudo ip addr add 172.16.0.1/24 dev tap0 -sudo ip link set tap0 up -sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward" -sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE -sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -sudo iptables -A FORWARD -i tap0 -o eth0 -j ACCEPT +sudo nft add table firecracker +sudo nft 'add chain firecracker postrouting { type nat hook postrouting priority srcnat; policy accept; }' +sudo nft 'add chain firecracker filter { type filter hook forward priority filter; policy accept; }' ``` -*Note:* The IP of the TAP device should be chosen such that it's not in the same -subnet as the IP address of the host. +The first rule we'll need will masquerade packets from the guest IP as if they +came from the host's IP, by changing the source IP address of these packets: -*Advanced:* If you are running multiple Firecracker MicroVMs in parallel, or -have something else on your system using `tap0` then you need to create a `tap` -for each one, with a unique name. +```bash +sudo nft add rule firecracker postrouting ip saddr 172.16.0.2 oifname eth0 counter masquerade +``` -*Advanced:* You also need to do the `iptables` set up for each new `tap`. If you -have `iptables` rules you care about on your host, you may want to save those -rules before starting. +The second rule we'll need will accept packets from the tap IP (the guest will +use the tap IP as its gateway and will therefore route its own packets through +the tap IP) and direct them to the host network interface: ```bash -sudo iptables-save > iptables.rules.old +sudo nft add rule firecracker filter iifname tap0 oifname eth0 accept +``` + +### Configuration via `iptables-nft` + +Tables and chains are managed by `iptables-nft` automatically, but we'll need +three rules to perform the NAT steps: + +```bash +sudo iptables-nft -t nat -A POSTROUTING -o eth0 -s 172.16.0.2 -j MASQUERADE +sudo iptables-nft -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +sudo iptables-nft -A FORWARD -i tap0 -o eth0 -j ACCEPT ``` ## Setting Up Firecracker @@ -85,14 +144,20 @@ configuration file like this: ``` Alternatively, if you are using firectl, add ---tap-device=tap0/06:00:AC:10:00:02\` to your command line. +`--tap-device=tap0/06:00:AC:10:00:02\` to your command line. ## In The Guest -Once you have booted the guest, bring up networking within the guest: +Once you have booted the guest, it will have its networking interface with the +name specified by `iface_id` in the Firecracker configuration. + +You'll now need to assign the guest its IP, activate the guest's networking +interface and set up the `tap` IP as the guest's gateway address, so that +packets are routed through the `tap` device, where they are then picked up by +the setup on the host prepared before: ```bash -ip addr add 172.16.0.2/24 dev eth0 +ip addr add 172.16.0.2/30 dev eth0 ip link set eth0 up ip route add default via 172.16.0.1 dev eth0 ``` @@ -107,23 +172,183 @@ your environment. For testing, you can add a public DNS server to nameserver 8.8.8.8 ``` -## \[Advanced\] Setting Up a Bridge Interface +**Note:** Sometimes, it's undesirable to have `iproute2` (providing the `ip` +command) installed on your guest OS, or you simply want to have these steps be +performed automatically. To do this, check out the _Advanced: Guest network +configuration using kernel command line_ section. + +## Cleaning up + +The first step to cleaning up is to delete the tap device on the host: + +```bash +sudo ip link del tap0 +``` + +### Cleanup using `nft` + +You'll want to delete the two nftables rules for NAT routing from the +`postrouting` and `filter` chains. To do this with nftables, you'll need to look +up the _handles_ (identifiers) of these rules by running: + +```bash +sudo nft -a list ruleset +``` + +Now, find the `# handle` comments relating to the two rules and delete them. For +example, if the handle to the masquerade rule is 1 and the one to the forwarding +rule is 2: + +```bash +sudo nft delete rule firecracker postrouting handle 1 +sudo nft delete rule firecracker filter handle 2 +``` + +Run the following steps only **if you have no more guests** running on the host: + +Set IPv4 forwarding back to disabled: + +```bash +echo 0 | sudo tee /proc/sys/net/ipv4/ip_forward +``` + +If you're using `nft`, delete the `firecracker` table to revert your nftables +configuration fully back to its initial state: + +```bash +sudo nft delete table firecracker +``` + +### Cleanup using `iptables-nft` + +Of the configured `iptables-nft` rules, two should be deleted if you have guests +remaining in your configuration: + +```bash +sudo iptables-nft -t nat -D POSTROUTING -o eth0 -s 172.16.0.2 -j MASQUERADE +sudo iptables-nft -D FORWARD -i tap0 -o eth0 -j ACCEPT +``` + +**If you have no more guests** running on the host, then similarly set IPv4 +forwarding back to disabled: + +```bash +echo 0 | sudo tee /proc/sys/net/ipv4/ip_forward +``` + +And delete the remaining `conntrack` rule that applies to all guests: + +```bash +sudo iptables-nft -D FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +``` + +If nothing else is using `iptables-nft` on the system, you may even want to +delete the entire system ruleset like so: + +```bash +sudo iptables-nft -F +sudo iptables-nft -t nat -F +``` + +## Advanced: Multiple guests + +To configure multiple guests, we will only need to repeat some of the steps in +this setup for each of the microVMs: + +1. Each microVM has its own subnet and the two IP addresses inside of it: the + `tap` IP and the guest IP. +1. Each microVM has its own two nftables rules for masquerading and forwarding, + while the same table and two chains can be shared between the microVMs. +1. Each microVM has its own routing configuration inside the guest itself + (achieved through `iproute2` or the method described in the _Advanced: Guest + network configuration at kernel level_ section). + +To give a more concrete example, **let's add a second microVM** to the one +you've already configured: + +Let's assume we allocate /30 subnets in the 172.16.0.0/16 range sequentially to +give out as few addresses as needed. + +The next /30 subnet in the 172.16.0.0/16 range will give us these two IPs: +172.16.0.5 as the `tap` IP and 172.16.0.6 as the guest IP. + +Our new `tap` device will, sequentially, have the name `tap1`: + +```bash +sudo ip tuntap add tap1 mode tap +sudo ip addr add 172.16.0.5/30 dev tap1 +sudo ip link set tap1 up +``` + +Now, let's add the new two `nft` rules, also with the new values: + +```bash +sudo nft add rule firecracker postrouting ip saddr 172.16.0.6 oifname eth0 counter masquerade +sudo nft add rule firecracker filter iifname tap1 oifname eth0 accept +``` + +If using `iptables-nft`, add the rules like so: + +```bash +sudo iptables-nft -t nat -A POSTROUTING -o eth0 -s 172.16.0.6 -j MASQUERADE +sudo iptables-nft -A FORWARD -i tap1 -o eth0 -j ACCEPT +``` + +Modify your Firecracker configuration with the `host_dev_name` now being `tap1` +instead of `tap0`, boot up the guest and perform the routing inside of it like +so, changing the guest IP and `tap` IP: + +```bash +ip addr add 172.16.0.6/30 dev eth0 +ip link set eth0 up +ip route add default via 172.16.0.5 dev eth0 +``` + +Or, you can use the setup from _Advanced: Guest network configuration at kernel +level_ by simply changing the G and T variables, i.e. the guest IP and `tap` IP. + +**Note:** if you'd like to calculate the guest and `tap` IPs using the +sequential subnet allocation method that has been used here, you can use the +following formulas specific to IPv4 addresses: + +`tap` IP = `172.16.[(A*O+1)/256].[(A*O+1)%256]`. + +Guest IP = `172.16.[(A*O+2)/256].[(A*O+2)%256]`. + +Round down the division and replace `A` with the amount of IP addresses inside +your subnet (for a /30 subnet, that will be 4 addresses, for example) and +replace `O` with the sequential number of your microVM, starting at 0. You can +replace `172.16` with any other values that fit between between 1 and 255 as +usual with an IPv4 address. + +For example, let's calculate the addresses of the 1000-th microVM with a /30 +subnet in the `172.16.0.0/16` range: + +`tap` IP = `172.16.[(4*999+1)/256].[(4*999+1)%256]` = `172.16.15.157`. + +Guest IP = `172.16.[(4*999+2)/256].[(4*999+2)%256]` = `172.16.15.158`. + +This allocation setup has been used successfully in the `firecracker-demo` +project for launching several thousand microVMs on the same host: +[relevant lines](https://github.com/firecracker-microvm/firecracker-demo/blob/63717c6e7fbd277bdec8e26a5533d53544a760bb/start-firecracker.sh#L45). + +## Advanced: Bridge-based routing ### On The Host -1. Create a bridge interface +1. Create a bridge interface: ```bash sudo ip link add name br0 type bridge ``` -1. Add tap interface [created above](#on-the-host) to the bridge +1. Add the `tap` device [created above](#on-the-host) to the bridge: ```bash sudo ip link set dev tap0 master br0 ``` -1. Define an IP address in your network for the bridge. +1. Define an IP address in your network for the bridge: For example, if your gateway were on `192.168.1.1` and you wanted to use this for getting dynamic IPs, you would want to give the bridge an unused IP @@ -133,24 +358,30 @@ nameserver 8.8.8.8 sudo ip address add 192.168.1.7/24 dev br0 ``` -1. Add firewall rules to allow traffic to be routed to the guest +1. Add a firewall rule to allow traffic to be routed to the guest: ```bash sudo iptables -t nat -A POSTROUTING -o br0 -j MASQUERADE ``` +1. Once you're cleaning up the configuration, make sure to delete the bridge: + + ```bash + sudo ip link del br0 + ``` + ### On The Guest 1. Define an unused IP address in the bridge's subnet e.g., `192.168.1.169/24`. - _Note: Alternatively, you could rely on DHCP for getting a dynamic IP address - from your gateway._ + **Note**: Alternatively, you could rely on DHCP for getting a dynamic IP + address from your gateway. ```bash ip addr add 192.168.1.169/24 dev eth0 ``` -1. Set the interface up. +1. Enable the network interface: ```bash ip link set eth0 up @@ -177,43 +408,38 @@ nameserver 8.8.8.8 192.168.1.1 via 192.168.1.7 dev eth0 ``` -1. Add your nameserver to `resolve.conf` +1. Add your nameserver to `/etc/resolve.conf` ```bash # cat /etc/resolv.conf nameserver 192.168.1.1 ``` -## Cleaning up +## Advanced: Guest network configuration using kernel command line -The first step to cleaning up is deleting the tap device: +The Linux kernel supports an `ip` CLI arguments that can be passed to it when +booting. Boot arguments in Firecracker are configured in the `boot_args` +property of the boot source (`boot-source` object in the JSON configuration or +the equivalent endpoint in the API server). -```bash -sudo ip link del tap0 -``` +The value of the `ip` CLI argument for our setup will be the of this format: +`G::T:GM::GI:off`. G is the guest IP (without the subnet), T is the `tap` IP +(without the subnet), GM is the "long" mask IP of the guest CIDR and GI is the +name of the guest network interface. -If you don't have anything else using `iptables` on your machine, clean up those -rules: - -```bash -sudo iptables -F -sudo sh -c "echo 0 > /proc/sys/net/ipv4/ip_forward" # usually the default -``` +Substituting our values, we get: +`ip=172.16.0.2::172.16.0.1:255.255.255.252::eth0:off`. Insert this at the end of +your boot arguments for your microVM, and the guest Linux kernel will +automatically perform the routing configuration done in the _In the Guest_ +section without needing `iproute2` installed in the guest. -If you have an existing iptables setup, you'll want to be more careful about -cleaning up. +As soon as you boot the guest, it will already be connected to the network +(assuming you correctly performing the other steps). -*Advanced:* If you saved your iptables rules in the first step, then you can -restore them like this: +**Note**: you can also use the `ip` argument to configure a primary DNS server +and, optionally, a second DNS server without needing to touch +`/etc/resolv.conf`. As an example: -```bash -if [ -f iptables.rules.old ]; then - sudo iptables-restore < iptables.rules.old -fi -``` - -*Advanced:* If you created a bridge interface, delete it using the following: - -```bash -sudo ip link del br0 -``` +`ip=172.16.0.2::172.16.0.1:255.255.255.252::eth0:off:8.8.8.8:1.1.1.1` configures +`8.8.8.8` as the primary DNS server and `1.1.1.1` as the secondary DNS server, +as well as the rest of the guest-side routing. diff --git a/docs/prod-host-setup.md b/docs/prod-host-setup.md index acf06a1e3d4..cf0ac86cf1f 100644 --- a/docs/prod-host-setup.md +++ b/docs/prod-host-setup.md @@ -254,18 +254,18 @@ echo "swap partitions present (Recommendation: no swap)" \ ### Mitigating hardware vulnerabilities -> \[!CAUTION\] +> [!CAUTION] > > Firecracker is not able to mitigate host's hardware vulnerabilities. Adequate > mitigations need to be put in place when configuring the host. -> \[!CAUTION\] +> [!CAUTION] > > Firecracker is designed to provide isolation boundaries between microVMs > running in different Firecracker processes. It is strongly recommended that > each Firecracker process corresponds to a workload of a single tenant. -> \[!CAUTION\] +> [!CAUTION] > > For security and stability reasons it is highly recommended to load updated > microcode as soon as possible. Aside from keeping the system firmware @@ -328,13 +328,16 @@ For vendor-specific recommendations, please consult the resources below: - ARM: [Speculative Processor Vulnerability](https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability) -##### \[ARM only\] Physical counter directly passed through to the guest +##### [ARM only] VM Physical counter behaviour -On ARM, the physical counter (i.e `CNTPCT`) it is returning the -[actual EL1 physical counter value of the host][1]. From the discussions before -merging this change [upstream][2], this seems like a conscious design decision -of the ARM code contributors, giving precedence to performance over the ability -to trap and control this in the hypervisor. +On ARM, Firecracker tries to reset the `CNTPCT` physical counter on VM boot. +This is done in order to prevent VM from reading host physical counter value. +Firecracker will only try to reset the counter if the host KVM contains +`KVM_CAP_COUNTER_OFFSET` capability. This capability is only present in kernels +containing +[this](https://lore.kernel.org/all/20230330174800.2677007-1-maz@kernel.org/) +patch series (starting from 6.4 and newer). For older kernels the counter value +will be passed through from the host. ##### Verification @@ -428,6 +431,3 @@ To validate that the change took effect, the file [^1]: Look for `GRUB_CMDLINE_LINUX` in file `/etc/default/grub` in RPM-based systems, and [this doc for Ubuntu](https://wiki.ubuntu.com/Kernel/KernelBootParameters). - -[1]: https://elixir.free-electrons.com/linux/v4.14.203/source/virt/kvm/arm/hyp/timer-sr.c#L63 -[2]: https://lists.cs.columbia.edu/pipermail/kvmarm/2017-January/023323.html diff --git a/docs/pvh.md b/docs/pvh.md new file mode 100644 index 00000000000..1cef74a2389 --- /dev/null +++ b/docs/pvh.md @@ -0,0 +1,15 @@ +# PVH boot mode + +Firecracker supports booting x86 kernels in "PVH direct boot" mode +[as specified by the Xen project](https://github.com/xen-project/xen/blob/master/docs/misc/pvh.pandoc). +If a kernel is provided which contains the XEN_ELFNOTE_PHYS32_ENTRY ELF Note +then this boot mode will be used. This boot mode was designed for virtualized +environments which load the kernel directly, and is simpler than the "Linux +boot" mode which is designed to be launched from a legacy boot loader. + +PVH boot mode can be enabled for Linux by setting `CONFIG_PVH=y` in the kernel +configuration. (This is not the default setting.) + +PVH boot mode is enabled by default in FreeBSD, which has support for +Firecracker starting with FreeBSD 14.0. Instructions on building a FreeBSD +kernel and root filesystem are available [here](rootfs-and-kernel-setup.md). diff --git a/docs/rootfs-and-kernel-setup.md b/docs/rootfs-and-kernel-setup.md index 4a64fcec1bb..8dc10f55f87 100644 --- a/docs/rootfs-and-kernel-setup.md +++ b/docs/rootfs-and-kernel-setup.md @@ -1,6 +1,6 @@ # Creating Custom rootfs and kernel Images -## Creating a kernel Image +## Creating a Linux kernel Image ### Manual compilation @@ -79,7 +79,7 @@ but without ACPI support) and `6.1`. After the command finishes, the kernels along with the corresponding KConfig used will be stored under `resources/$(uname -m)`. -## Creating a rootfs Image +## Creating a Linux rootfs Image A rootfs image is just a file system image, that hosts at least an init system. For instance, our getting started guide uses an ext4 filesystem image. Note @@ -185,3 +185,44 @@ adjust the script(s) to suit your use case. You should now have a rootfs image (`ubuntu-22.04.ext4`), that you can boot with Firecracker. + +## Creating FreeBSD rootfs and kernel Images + +Here's a quick step-by-step guide to building a FreeBSD rootfs and kernel that +Firecracker can boot: + +1. Boot a FreeBSD system. In EC2, the + [FreeBSD 13 Marketplace image](https://aws.amazon.com/marketplace/pp/prodview-ukzmy5dzc6nbq) + is a good option; you can also use weekly snapshot AMIs published by the + FreeBSD project. (Firecracker support is in FreeBSD 14 and later, so you'll + need FreeBSD 13 or later to build it.) + + The build will require about 50 GB of disk space, so size the disk + appropriately. + +1. Log in to the FreeBSD system and become root. If using EC2, you'll want to + ssh in as `ec2-user` with your chosen SSH key and then `su` to become root. + +1. Install git and check out the FreeBSD src tree: + + ```sh + pkg install -y git + git clone https://git.freebsd.org/src.git /usr/src + ``` + + Firecracker support is available since FreeBSD 14.0 (released November 2023). + +1. Build FreeBSD: + + ```sh + make -C /usr/src buildworld buildkernel KERNCONF=FIRECRACKER + make -C /usr/src/release firecracker DESTDIR=`pwd` + ``` + +You should now have a rootfs `freebsd-rootfs.bin` and a kernel +`freebsd-kern.bin` in the current directory (or elsewhere if you change the +`DESTDIR` value) that you can boot with Firecracker. Note that the FreeBSD +rootfs generated in this manner is somewhat minimized compared to "stock" +FreeBSD; it omits utilities which are only relevant on physical systems (e.g., +utilities related to floppy disks, USB devices, and some network interfaces) and +also debug files and the system compiler. diff --git a/docs/seccomp.md b/docs/seccomp.md index dac55006bc5..06c7fd92d76 100644 --- a/docs/seccomp.md +++ b/docs/seccomp.md @@ -11,8 +11,10 @@ follows: - API - right before launching the HTTP server; - VCPUs - right before executing guest code. -**Note**: On experimental GNU targets, there are no default seccomp filters -installed, since they are not intended for production use. +> [!WARNING] +> +> On debug binaries and experimental GNU targets, there are no default seccomp +> filters installed, since they are not intended for production use. Firecracker uses JSON files for expressing the filter rules and relies on the [seccompiler](seccompiler.md) tool for all the seccomp functionality. @@ -58,6 +60,12 @@ Potential use cases: - Users of experimentally-supported targets (like GNU libc builds) may be able to use this feature to implement seccomp filters without needing to have a custom build of Firecracker. +- Users of debug binaries who need to use a seccomp filter for any reason will + be able to use this feature to implement seccomp filters without needing to + have a custom build of Firecracker. Note: there may be some differences in + syscalls between `debug` and `release` builds. A non-comprehensive list is: + - `fcntl(F_GETFD)` is used by debug assertions to verify a dropped `fd` is + valid. - Faced with a _theoretical_ production issue, due to a syscall that was issued by the Firecracker process, but not allowed by the seccomp policy, one may use a custom filter in order to quickly mitigate the issue. This can speed up the diff --git a/docs/snapshotting/handling-page-faults-on-snapshot-resume.md b/docs/snapshotting/handling-page-faults-on-snapshot-resume.md index ea790624a18..a5da980124e 100644 --- a/docs/snapshotting/handling-page-faults-on-snapshot-resume.md +++ b/docs/snapshotting/handling-page-faults-on-snapshot-resume.md @@ -162,7 +162,7 @@ connect/send data. ### Example An example of a handler process can be found -[here](../../src/firecracker/examples/uffd/valid_handler.rs). The process is +[here](../../src/firecracker/examples/uffd/on_demand_handler.rs). The process is designed to tackle faults on a certain address by loading into memory the entire region that the address belongs to, but users can choose any other behavior that suits their use case best. diff --git a/docs/snapshotting/network-for-clones.md b/docs/snapshotting/network-for-clones.md index d0a44e89413..df99f782e38 100644 --- a/docs/snapshotting/network-for-clones.md +++ b/docs/snapshotting/network-for-clones.md @@ -3,7 +3,7 @@ This document presents a strategy to ensure continued network connectivity for multiple clones created from a single Firecracker microVM snapshot. -> \[!CAUTION\] +> [!CAUTION] > > This should be considered as just an example to get you started, and we don't > claim this is a performant or secure setup. @@ -142,6 +142,63 @@ Otherwise, packets originating from the guest might be using old Link Layer Address for up to arp cache timeout seconds. After said timeout period, connectivity will work both ways even without an explicit flush. +### Renaming host device names + +In some environments where the jailer is not being used, restoring a snapshot +may be tricky because the tap device on the host will not be the same as the tap +device that the original VM was mapped to when it was snapshotted, for example +when the tap device comes from a pool of such devices. + +In this case you can use the `network_overrides` parameter of the snapshot +restore API to specify which guest network device maps to which host tap device. + +For example, if we have a network interface named `eth0` in the snapshotted +microVM, we can override it to point to the host device `vmtap01` during +snapshot resume, like this: + +```bash +curl --unix-socket /tmp/firecracker.socket -i \ + -X PUT 'http://localhost/snapshot/load' \ + -H 'Accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "snapshot_path": "./snapshot_file", + "mem_backend": { + "backend_path": "./mem_file", + "backend_type": "File" + }, + "network_overrides": [ + { + "iface_id": "eth0", + "host_dev_name": "vmtap01" + } + ] + }' +``` + +This may require reconfiguration of the networking inside the VM so that it is +still routable externally. +[network setup documentation](../network-setup.md#in-the-guest) describes what +the typical setup is. If you are not using network namespaces or the jailer, +then the guest will have to be made aware (via vsock or other channel) that it +needs to reconfigure its network to match the network configured on the tap +device. + +If the new TAP device, say `vmtap3` has been configured to use a guest address +of `172.16.3.2` then after snapshot restore you would run something like: + +```bash +# In the guest + +# Clear out the previous addr and route +ip addr flush dev eth0 +ip route flush dev eth0 + +# Configure the new address +ip addr add 172.16.3.2/30 dev eth0 +ip route add default via 172.16.3.1/30 dev eth0 +``` + # Ingress connectivity The above setup only provides egress connectivity. If in addition we also want diff --git a/docs/snapshotting/snapshot-support.md b/docs/snapshotting/snapshot-support.md index a9e0bc8dc4c..18d3799da2d 100644 --- a/docs/snapshotting/snapshot-support.md +++ b/docs/snapshotting/snapshot-support.md @@ -25,6 +25,8 @@ - [Secure and insecure usage examples](#usage-examples) - [Reusing snapshotted states securely](#reusing-snapshotted-states-securely) - [Vsock device limitation](#vsock-device-limitation) +- [VMGenID device limitation](#vmgenid-device-limitation) +- [Where can I resume my snapshots?](#where-can-i-resume-my-snapshots) ## About microVM snapshotting @@ -37,7 +39,7 @@ workload at that particular point in time. ### Supported platforms -> \[!WARNING\] +> [!WARNING] > > The Firecracker snapshot feature is in > [developer preview](../RELEASE_POLICY.md) on all CPU micro-architectures @@ -55,7 +57,10 @@ creation process). Both network and vsock packet loss can be expected on guests that are resumed from snapshots in another Firecracker process. It is also not guaranteed that -the state of the network connections survives the process. +the state of the network connections survives the process. Furthermore, vsock +connections that are open when the snapshot is taken are closed, but existing +vsock listen sockets in the guest still remain active and can accept new +connections after resume (see [Vsock device reset](#vsock-device-reset)). In order to make restoring possible, Firecracker snapshots save the full state of the following resources: @@ -121,17 +126,6 @@ The snapshot functionality is still in developer preview due to the following: ### Limitations -- Currently on aarch64 platforms only lower 128 bits of any register are saved - due to the limitations of `get/set_one_reg` from `kvm-ioctls` crate that - Firecracker uses to interact with KVM. This creates an issue with newer - aarch64 CPUs with support for registers with width greater than 128 bits, - because these registers will be truncated before being stored in the snapshot. - This can lead to uVM failure if restored from such snapshot. Because registers - wider than 128 bits are usually used in SVE instructions, the best way to - mitigate this issue is to ensure that the software run in uVM does not use SVE - instructions during snapshot creation. An alternative way is to use - [CPU templates](../cpu_templates/cpu-templates.md) to disable SVE related - features in uVM. - High snapshot latency on 5.4+ host kernels due to cgroups V1. We strongly recommend to deploy snapshots on cgroups V2 enabled hosts for the implied kernel versions - @@ -139,8 +133,6 @@ The snapshot functionality is still in developer preview due to the following: - Guest network connectivity is not guaranteed to be preserved after resume. For recommendations related to guest network connectivity for clones please see [Network connectivity for clones](network-for-clones.md). -- Vsock device does not have full snapshotting support. Please see - [Vsock device limitation](#vsock-device-limitation). - Snapshotting on arm64 works for both GICv2 and GICv3 enabled guests. However, restoring between different GIC version is not possible. - If a [CPU template](../cpu_templates/cpu-templates.md) is not used on x86_64, @@ -266,7 +258,7 @@ curl --unix-socket /tmp/firecracker.socket -i \ -d '{ "snapshot_type": "Full", "snapshot_path": "./snapshot_file", - "mem_file_path": "./mem_file", + "mem_file_path": "./mem_file" }' ``` @@ -604,29 +596,19 @@ identifiers, cached random numbers, cryptographic tokens, etc **will** still be replicated across multiple microVMs resumed from the same snapshot. Users need to implement mechanisms for ensuring de-duplication of such state, where needed. -## Vsock device limitation - -Vsock must be inactive during snapshot. Vsock device can break if snapshotted -while having active connections. Firecracker snapshots do not capture any -inflight network or vsock (through the linux unix domain socket backend) traffic -that has left or not yet entered Firecracker. - -The above, coupled with the fact that Vsock control protocol is not resilient to -vsock packet loss, leads to Vsock device breakage when doing a snapshot while -there are active Vsock connections. +## Vsock device reset -As a solution to the above issue, active Vsock connections prior to snapshotting -the VM are forcibly closed by sending a specific event called -`VIRTIO_VSOCK_EVENT_TRANSPORT_RESET`. The event is sent on `SnapshotCreate`. On +The vsock device is reset across snapshot/restore to avoid inconsistent state +between device and driver leading to breakage +([#2218](https://github.com/firecracker-microvm/firecracker/issues/2218)). This +is done by sending a `VIRTIO_VSOCK_EVENT_TRANSPORT_RESET` event to the guest +driver during `SnapshotCreate` +([#2562](https://github.com/firecracker-microvm/firecracker/pull/2562)). On `SnapshotResume`, when the VM becomes active again, the vsock driver closes all -existing connections. Listen sockets still remain active. Users wanting to build -vsock applications that use the snapshot capability have to take this into -consideration. More details about this event can be found in the official Virtio -document [here](https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf), -section 5.10.6.6 Device Events. - -Firecracker handles sending the `reset` event to the vsock driver, thus the -customers are no longer responsible for closing active connections. +existing connections. Existing listen sockets still remain active, but their CID +is updated to reflect the current `guest_cid`. More details about this event can +be found in the official Virtio document +[here](https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-4080006). ## VMGenID device limitation @@ -638,28 +620,19 @@ might not be able to handle the injected notification and crash. We suggest to users that they take snapshots only after the guest kernel has completed booting, to avoid this issue. -## Snapshot compatibility across kernel versions - -We have a mechanism in place to experiment with snapshot compatibility across -supported host kernel versions by generating snapshot artifacts through -[this tool](../../tools/create_snapshot_artifact) and checking devices' -functionality using -[this test](../../tests/integration_tests/functional/test_snapshot_restore_cross_kernel.py). -The test restores the snapshot and ensures that all the devices set-up (network -devices, disk, vsock, balloon and MMDS) are operational post-load. - -In those tests the instance is fixed, except some combinations where we also -test across the same CPU family (Intel x86, Gravitons). In general cross-CPU -snapshots [are not supported](./versioning.md#cpu-model) +## Where can I resume my snapshots? -The tables below reflect the snapshot compatibility observed on the AWS -instances we support. +Snapshots must be resumed on an software and hardware configuration which is +identical to what they were generated on. However, in limited cases, snapshots +can be resumed on identical hardware instances where they were taken on, but +using newer host kernel versions. While we do not provide any guarantees on this +setup (and do not recommend doing this in production), we are currently aware of +the compatibility table reported below: -**all** means all currently supported Intel/AMD/ARM metal instances (m6g, m7g, -m5n, c5n, m6i, m6a). It does not mean cross-instance, i.e. a snapshot taken on -m6i won't work on an m6g instance. +| .metal instance type | taken on host kernel | restored on host kernel | +| -------------------- | -------------------- | ----------------------- | +| {c5n,m5n,m6i,m6a} | 5.10 | 6.1 | -| *CPU family* | *taken on host kernel* | *restored on host kernel* | *working?* | -| ------------ | ---------------------- | ------------------------- | ---------- | -| **x86_64** | 5.10 | 6.1 | Y | -| **x86_64** | 6.1 | 5.10 | N | +For example, a snapshot taken on a m6i.metal host running a 5.10 host kernel can +be restored on a different m6i.metal host running a 6.1 host kernel (but not +vice versa), but could not be restored on a c5n.metal host. diff --git a/docs/tracing.md b/docs/tracing.md index ede82b148be..dd9f284c595 100644 --- a/docs/tracing.md +++ b/docs/tracing.md @@ -46,7 +46,7 @@ clippy-tracing \ --action fix \ --path ./src \ --exclude benches \ - --exclude virtio/gen,bindings.rs,net/gen \ + --exclude virtio/generated,bindings.rs,net/generated \ --exclude log-instrument-macros/,log-instrument/,clippy-tracing/ \ --exclude vmm_config/logger.rs,logger/,signal_handler.rs,time.rs ``` diff --git a/resources/guest_configs/ci.config b/resources/guest_configs/ci.config new file mode 100644 index 00000000000..166ab94db3f --- /dev/null +++ b/resources/guest_configs/ci.config @@ -0,0 +1,14 @@ +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_MSDOS_PARTITION=y +CONFIG_SQUASHFS_ZSTD=y +# aarch64 only TBD split into a separate file +CONFIG_DEVMEM=y +# CONFIG_ARM64_ERRATUM_3194386 is not set +# Needed for CTRL+ALT+DEL support +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_GSCPS2=y +CONFIG_KEYBOARD_ATKBD=y +CONFIG_INPUT_KEYBOARD=y \ No newline at end of file diff --git a/resources/guest_configs/debug.config b/resources/guest_configs/debug.config new file mode 100644 index 00000000000..6ff73f2162b --- /dev/null +++ b/resources/guest_configs/debug.config @@ -0,0 +1,5 @@ +CONFIG_FRAME_POINTER=y +# CONFIG_KGDB=y +# CONFIG_KGDB_SERIAL_CONSOLE=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_INFO_DWARF4=y diff --git a/resources/guest_configs/ftrace.config b/resources/guest_configs/ftrace.config new file mode 100644 index 00000000000..b180c7f232a --- /dev/null +++ b/resources/guest_configs/ftrace.config @@ -0,0 +1,11 @@ +CONFIG_FTRACE=y +CONFIG_FUNCTION_TRACER=y +CONFIG_FUNCTION_GRAPH_TRACER=y +CONFIG_IRQSOFF_TRACER=y +CONFIG_PREEMPT_TRACER=y +CONFIG_SCHED_TRACER=y +CONFIG_STACK_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_FUNCTION_PROFILER=y +CONFIG_FTRACE_MCOUNT_RECORD=y +CONFIG_FTRACE_SYSCALLS=y diff --git a/resources/guest_configs/patches/0001-fix-Adjust-config-options-for-what-is-needed-by-our-.patch b/resources/guest_configs/patches/0001-fix-Adjust-config-options-for-what-is-needed-by-our-.patch deleted file mode 100644 index ed1f683dce1..00000000000 --- a/resources/guest_configs/patches/0001-fix-Adjust-config-options-for-what-is-needed-by-our-.patch +++ /dev/null @@ -1,98 +0,0 @@ -From 85e406b23c086c446ccfd6f8c83670800cc4de3a Mon Sep 17 00:00:00 2001 -From: Patrick Roy -Date: Tue, 30 Jul 2024 13:59:04 +0100 -Subject: [PATCH] fix: Adjust config options for what is needed by our tests - -Additionally, set `CONFIG_MSDOS_PARTITION=y` for our PARTUUID tests, -`CONFIG_DEVMEM=y` on aarch64/5.10 (for various integration tests that -rely on /dev/mem), and CONFIG_IKCONFIG=y and CONFIG_IKCONFIG_PROC=y for -the spectre-meltdown-checker.sh tests to work on AMD. - -Signed-off-by: Patrick Roy ---- - .../guest_configs/microvm-kernel-ci-aarch64-5.10.config | 7 ++++--- - .../microvm-kernel-ci-x86_64-5.10-no-acpi.config | 5 +++-- - .../guest_configs/microvm-kernel-ci-x86_64-5.10.config | 5 +++-- - 3 files changed, 10 insertions(+), 7 deletions(-) - -diff --git a/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config b/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config -index ac44904c..0555055c 100644 ---- a/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config -+++ b/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config -@@ -110,7 +110,8 @@ CONFIG_RCU_STALL_COMMON=y - CONFIG_RCU_NEED_SEGCBLIST=y - # end of RCU Subsystem - --# CONFIG_IKCONFIG is not set -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y - # CONFIG_IKHEADERS is not set - CONFIG_LOG_BUF_SHIFT=17 - CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 -@@ -752,7 +753,7 @@ CONFIG_PARTITION_ADVANCED=y - # CONFIG_AMIGA_PARTITION is not set - # CONFIG_ATARI_PARTITION is not set - # CONFIG_MAC_PARTITION is not set --# CONFIG_MSDOS_PARTITION is not set -+CONFIG_MSDOS_PARTITION=y - # CONFIG_LDM_PARTITION is not set - # CONFIG_SGI_PARTITION is not set - # CONFIG_ULTRIX_PARTITION is not set -@@ -1726,7 +1727,7 @@ CONFIG_HW_RANDOM_VIRTIO=y - # CONFIG_HW_RANDOM_CCTRNG is not set - # CONFIG_HW_RANDOM_XIPHERA is not set - # CONFIG_HW_RANDOM_GRAVITON is not set --# CONFIG_DEVMEM is not set -+CONFIG_DEVMEM=y - # CONFIG_RAW_DRIVER is not set - # CONFIG_TCG_TPM is not set - # CONFIG_XILLYBUS is not set -diff --git a/resources/guest_configs/microvm-kernel-ci-x86_64-5.10-no-acpi.config b/resources/guest_configs/microvm-kernel-ci-x86_64-5.10-no-acpi.config -index b87fb3e4..a27c1f84 100644 ---- a/resources/guest_configs/microvm-kernel-ci-x86_64-5.10-no-acpi.config -+++ b/resources/guest_configs/microvm-kernel-ci-x86_64-5.10-no-acpi.config -@@ -134,7 +134,8 @@ CONFIG_RCU_NEED_SEGCBLIST=y - # end of RCU Subsystem - - CONFIG_BUILD_BIN2C=y --# CONFIG_IKCONFIG is not set -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y - # CONFIG_IKHEADERS is not set - CONFIG_LOG_BUF_SHIFT=17 - CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 -@@ -702,7 +703,7 @@ CONFIG_PARTITION_ADVANCED=y - # CONFIG_AMIGA_PARTITION is not set - # CONFIG_ATARI_PARTITION is not set - # CONFIG_MAC_PARTITION is not set --# CONFIG_MSDOS_PARTITION is not set -+CONFIG_MSDOS_PARTITION=y - # CONFIG_LDM_PARTITION is not set - # CONFIG_SGI_PARTITION is not set - # CONFIG_ULTRIX_PARTITION is not set -diff --git a/resources/guest_configs/microvm-kernel-ci-x86_64-5.10.config b/resources/guest_configs/microvm-kernel-ci-x86_64-5.10.config -index 09461c17..af9ec662 100644 ---- a/resources/guest_configs/microvm-kernel-ci-x86_64-5.10.config -+++ b/resources/guest_configs/microvm-kernel-ci-x86_64-5.10.config -@@ -127,7 +127,8 @@ CONFIG_RCU_NEED_SEGCBLIST=y - # end of RCU Subsystem - - CONFIG_BUILD_BIN2C=y --# CONFIG_IKCONFIG is not set -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y - # CONFIG_IKHEADERS is not set - CONFIG_LOG_BUF_SHIFT=17 - CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 -@@ -739,7 +740,7 @@ CONFIG_PARTITION_ADVANCED=y - # CONFIG_AMIGA_PARTITION is not set - # CONFIG_ATARI_PARTITION is not set - # CONFIG_MAC_PARTITION is not set --# CONFIG_MSDOS_PARTITION is not set -+CONFIG_MSDOS_PARTITION=y - # CONFIG_LDM_PARTITION is not set - # CONFIG_SGI_PARTITION is not set - # CONFIG_ULTRIX_PARTITION is not set --- -2.34.1 - diff --git a/resources/guest_configs/patches/0002-ci-adjust-6.1-guest-kernel-configs-for-our-CI.patch b/resources/guest_configs/patches/0002-ci-adjust-6.1-guest-kernel-configs-for-our-CI.patch deleted file mode 100644 index 06604fd104a..00000000000 --- a/resources/guest_configs/patches/0002-ci-adjust-6.1-guest-kernel-configs-for-our-CI.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 911b916fdc53acfc7229302b029fd615f1e670cf Mon Sep 17 00:00:00 2001 -From: Babis Chalios -Date: Fri, 23 Aug 2024 12:54:26 +0200 -Subject: [PATCH 2/2] ci: adjust 6.1 guest kernel configs for our CI - -Set CONFIG_MSDOS_PARTITION=y for our PARTUUID tests, -CONFIG_DEVMEM=y on aarch64 for various integration tests that rely on -/dev/mem being there and CONFIG_IKCONFIG=y, CONFIG_IKCONFIG_PROC=y for -the spectre-meltdown-checker.sh tests to work on AMD. - -Signed-off-by: Babis Chalios ---- - .../guest_configs/microvm-kernel-ci-aarch64-6.1.config | 7 ++++--- - .../guest_configs/microvm-kernel-ci-x86_64-6.1.config | 7 ++++--- - 2 files changed, 8 insertions(+), 6 deletions(-) - -diff --git a/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config b/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config -index 26b87a65..5c0334f4 100644 ---- a/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config -+++ b/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config -@@ -140,7 +140,8 @@ CONFIG_RCU_STALL_COMMON=y - CONFIG_RCU_NEED_SEGCBLIST=y - # end of RCU Subsystem - --# CONFIG_IKCONFIG is not set -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y - # CONFIG_IKHEADERS is not set - CONFIG_LOG_BUF_SHIFT=17 - CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 -@@ -753,7 +754,7 @@ CONFIG_PARTITION_ADVANCED=y - # CONFIG_AMIGA_PARTITION is not set - # CONFIG_ATARI_PARTITION is not set - # CONFIG_MAC_PARTITION is not set --# CONFIG_MSDOS_PARTITION is not set -+CONFIG_MSDOS_PARTITION=y - # CONFIG_LDM_PARTITION is not set - # CONFIG_SGI_PARTITION is not set - # CONFIG_ULTRIX_PARTITION is not set -@@ -1823,7 +1824,7 @@ CONFIG_HW_RANDOM_VIRTIO=y - # CONFIG_HW_RANDOM_XIPHERA is not set - CONFIG_HW_RANDOM_ARM_SMCCC_TRNG=y - # CONFIG_HW_RANDOM_GRAVITON is not set --# CONFIG_DEVMEM is not set -+CONFIG_DEVMEM=y - # CONFIG_TCG_TPM is not set - # CONFIG_XILLYBUS is not set - CONFIG_RANDOM_TRUST_CPU=y -diff --git a/resources/guest_configs/microvm-kernel-ci-x86_64-6.1.config b/resources/guest_configs/microvm-kernel-ci-x86_64-6.1.config -index 967e3203..8a2769c0 100644 ---- a/resources/guest_configs/microvm-kernel-ci-x86_64-6.1.config -+++ b/resources/guest_configs/microvm-kernel-ci-x86_64-6.1.config -@@ -162,8 +162,9 @@ CONFIG_RCU_STALL_COMMON=y - CONFIG_RCU_NEED_SEGCBLIST=y - # end of RCU Subsystem - --# CONFIG_IKCONFIG is not set --# CONFIG_IKHEADERS is not set -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+# CONFIG_IKHEADERS=y - CONFIG_LOG_BUF_SHIFT=17 - CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 - CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13 -@@ -769,7 +770,7 @@ CONFIG_PARTITION_ADVANCED=y - # CONFIG_AMIGA_PARTITION is not set - # CONFIG_ATARI_PARTITION is not set - # CONFIG_MAC_PARTITION is not set --# CONFIG_MSDOS_PARTITION is not set -+CONFIG_MSDOS_PARTITION=y - # CONFIG_LDM_PARTITION is not set - # CONFIG_SGI_PARTITION is not set - # CONFIG_ULTRIX_PARTITION is not set --- -2.34.1 - diff --git a/resources/guest_configs/patches/0003-enable-ftrace.patch b/resources/guest_configs/patches/0003-enable-ftrace.patch deleted file mode 100644 index a836a6fb3a7..00000000000 --- a/resources/guest_configs/patches/0003-enable-ftrace.patch +++ /dev/null @@ -1,115 +0,0 @@ -diff --git a/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config b/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config -index ac44904c1..2a6f2310f 100644 ---- a/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config -+++ b/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config -@@ -3089,7 +3089,17 @@ CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y - CONFIG_HAVE_SYSCALL_TRACEPOINTS=y - CONFIG_HAVE_C_RECORDMCOUNT=y - CONFIG_TRACING_SUPPORT=y --# CONFIG_FTRACE is not set -+CONFIG_FTRACE=y -+CONFIG_FUNCTION_TRACER=y -+CONFIG_FUNCTION_GRAPH_TRACER=y -+CONFIG_IRQSOFF_TRACER=y -+CONFIG_PREEMPT_TRACER=y -+CONFIG_SCHED_TRACER=y -+CONFIG_STACK_TRACER=y -+CONFIG_BLK_DEV_IO_TRACE=y -+CONFIG_FUNCTION_PROFILER=y -+CONFIG_FTRACE_MCOUNT_RECORD=y -+CONFIG_FTRACE_SYSCALLS=y - # CONFIG_SAMPLES is not set - CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y - # CONFIG_STRICT_DEVMEM is not set -diff --git a/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config b/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config -index 26b87a658..f0f765298 100644 ---- a/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config -+++ b/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config -@@ -3309,7 +3309,17 @@ CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y - CONFIG_HAVE_SYSCALL_TRACEPOINTS=y - CONFIG_HAVE_C_RECORDMCOUNT=y - CONFIG_TRACING_SUPPORT=y --# CONFIG_FTRACE is not set -+CONFIG_FTRACE=y -+CONFIG_FUNCTION_TRACER=y -+CONFIG_FUNCTION_GRAPH_TRACER=y -+CONFIG_IRQSOFF_TRACER=y -+CONFIG_PREEMPT_TRACER=y -+CONFIG_SCHED_TRACER=y -+CONFIG_STACK_TRACER=y -+CONFIG_BLK_DEV_IO_TRACE=y -+CONFIG_FUNCTION_PROFILER=y -+CONFIG_FTRACE_MCOUNT_RECORD=y -+CONFIG_FTRACE_SYSCALLS=y - # CONFIG_SAMPLES is not set - # CONFIG_STRICT_DEVMEM is not set - -diff --git a/resources/guest_configs/microvm-kernel-ci-x86_64-5.10-no-acpi.config b/resources/guest_configs/microvm-kernel-ci-x86_64-5.10-no-acpi.config -index b87fb3e44..fc45dda19 100644 ---- a/resources/guest_configs/microvm-kernel-ci-x86_64-5.10-no-acpi.config -+++ b/resources/guest_configs/microvm-kernel-ci-x86_64-5.10-no-acpi.config -@@ -2905,7 +2905,17 @@ CONFIG_HAVE_SYSCALL_TRACEPOINTS=y - CONFIG_HAVE_FENTRY=y - CONFIG_HAVE_C_RECORDMCOUNT=y - CONFIG_TRACING_SUPPORT=y --# CONFIG_FTRACE is not set -+CONFIG_FTRACE=y -+CONFIG_FUNCTION_TRACER=y -+CONFIG_FUNCTION_GRAPH_TRACER=y -+CONFIG_IRQSOFF_TRACER=y -+CONFIG_PREEMPT_TRACER=y -+CONFIG_SCHED_TRACER=y -+CONFIG_STACK_TRACER=y -+CONFIG_BLK_DEV_IO_TRACE=y -+CONFIG_FUNCTION_PROFILER=y -+CONFIG_FTRACE_MCOUNT_RECORD=y -+CONFIG_FTRACE_SYSCALLS=y - # CONFIG_SAMPLES is not set - CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y - CONFIG_STRICT_DEVMEM=y -diff --git a/resources/guest_configs/microvm-kernel-ci-x86_64-5.10.config b/resources/guest_configs/microvm-kernel-ci-x86_64-5.10.config -index 09461c178..6d85bce2c 100644 ---- a/resources/guest_configs/microvm-kernel-ci-x86_64-5.10.config -+++ b/resources/guest_configs/microvm-kernel-ci-x86_64-5.10.config -@@ -2987,7 +2987,17 @@ CONFIG_HAVE_SYSCALL_TRACEPOINTS=y - CONFIG_HAVE_FENTRY=y - CONFIG_HAVE_C_RECORDMCOUNT=y - CONFIG_TRACING_SUPPORT=y --# CONFIG_FTRACE is not set -+CONFIG_FTRACE=y -+CONFIG_FUNCTION_TRACER=y -+CONFIG_FUNCTION_GRAPH_TRACER=y -+CONFIG_IRQSOFF_TRACER=y -+CONFIG_PREEMPT_TRACER=y -+CONFIG_SCHED_TRACER=y -+CONFIG_STACK_TRACER=y -+CONFIG_BLK_DEV_IO_TRACE=y -+CONFIG_FUNCTION_PROFILER=y -+CONFIG_FTRACE_MCOUNT_RECORD=y -+CONFIG_FTRACE_SYSCALLS=y - # CONFIG_SAMPLES is not set - CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y - CONFIG_STRICT_DEVMEM=y -diff --git a/resources/guest_configs/microvm-kernel-ci-x86_64-6.1.config b/resources/guest_configs/microvm-kernel-ci-x86_64-6.1.config -index 967e32031..d11ef968a 100644 ---- a/resources/guest_configs/microvm-kernel-ci-x86_64-6.1.config -+++ b/resources/guest_configs/microvm-kernel-ci-x86_64-6.1.config -@@ -3185,7 +3185,17 @@ CONFIG_HAVE_OBJTOOL_MCOUNT=y - CONFIG_HAVE_C_RECORDMCOUNT=y - CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y - CONFIG_TRACING_SUPPORT=y --# CONFIG_FTRACE is not set -+CONFIG_FTRACE=y -+CONFIG_FUNCTION_TRACER=y -+CONFIG_FUNCTION_GRAPH_TRACER=y -+CONFIG_IRQSOFF_TRACER=y -+CONFIG_PREEMPT_TRACER=y -+CONFIG_SCHED_TRACER=y -+CONFIG_STACK_TRACER=y -+CONFIG_BLK_DEV_IO_TRACE=y -+CONFIG_FUNCTION_PROFILER=y -+CONFIG_FTRACE_MCOUNT_RECORD=y -+CONFIG_FTRACE_SYSCALLS=y - # CONFIG_SAMPLES is not set - CONFIG_HAVE_SAMPLE_FTRACE_DIRECT=y - CONFIG_HAVE_SAMPLE_FTRACE_DIRECT_MULTI=y diff --git a/resources/guest_configs/patches/0004-disable-CONFIG_ARM64_ERRATUM_3194386-for-aarch64.patch b/resources/guest_configs/patches/0004-disable-CONFIG_ARM64_ERRATUM_3194386-for-aarch64.patch deleted file mode 100644 index f3f16925e41..00000000000 --- a/resources/guest_configs/patches/0004-disable-CONFIG_ARM64_ERRATUM_3194386-for-aarch64.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config b/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config -index ac44904c1..9f912a70b 100644 ---- a/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config -+++ b/resources/guest_configs/microvm-kernel-ci-aarch64-5.10.config -@@ -341,6 +341,7 @@ CONFIG_ARM64_ERRATUM_1463225=y - CONFIG_ARM64_ERRATUM_1542419=y - CONFIG_ARM64_ERRATUM_1508412=y - CONFIG_ARM64_ERRATUM_2457168=y -+# CONFIG_ARM64_ERRATUM_3194386 is not set - CONFIG_CAVIUM_ERRATUM_22375=y - CONFIG_CAVIUM_ERRATUM_23144=y - CONFIG_CAVIUM_ERRATUM_23154=y -diff --git a/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config b/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config -index 26b87a658..29fe130f2 100644 ---- a/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config -+++ b/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config -@@ -362,6 +362,7 @@ CONFIG_ARM64_ERRATUM_2441009=y - CONFIG_ARM64_ERRATUM_2457168=y - CONFIG_ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD=y - CONFIG_ARM64_ERRATUM_2966298=y -+# CONFIG_ARM64_ERRATUM_3194386 is not set - CONFIG_CAVIUM_ERRATUM_22375=y - CONFIG_CAVIUM_ERRATUM_23144=y - CONFIG_CAVIUM_ERRATUM_23154=y diff --git a/resources/overlay/usr/local/bin/fast_page_fault_helper.c b/resources/overlay/usr/local/bin/fast_page_fault_helper.c index d304b97f94d..7558f7b09fc 100644 --- a/resources/overlay/usr/local/bin/fast_page_fault_helper.c +++ b/resources/overlay/usr/local/bin/fast_page_fault_helper.c @@ -10,35 +10,70 @@ // This way, the `memset` will trigger a fast page fault for every page in // the memory region. -#include // perror +#include // perror, fopen, fprintf #include // sigwait and friends #include // memset #include // mmap +#include // clock_gettime +#include // open #define MEM_SIZE_MIB (128 * 1024 * 1024) +#define NANOS_PER_SEC 1000000000 +#define PAGE_SIZE 4096 -int main(int argc, char *const argv[]) { +void touch_memory(void *mem, size_t size, char val) { + void *end = mem + size; + for (; mem < end; mem += PAGE_SIZE) { + *((char *)mem) = val; + } +} + +int main() { sigset_t set; int signal; + void *ptr; + struct timespec start, end; + long duration_nanos; + FILE *out_file; sigemptyset(&set); - if(sigaddset(&set, SIGUSR1) == -1) { + if (sigaddset(&set, SIGUSR1) == -1) { perror("sigaddset"); - return -1; + return 1; + } + if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) { + perror("sigprocmask"); + return 1; } - void *ptr = mmap(NULL, MEM_SIZE_MIB, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - - memset(ptr, 1, MEM_SIZE_MIB); + ptr = mmap(NULL, MEM_SIZE_MIB, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if(MAP_FAILED == ptr) { + if (MAP_FAILED == ptr) { perror("mmap"); - return -1; + return 1; } + touch_memory(ptr, MEM_SIZE_MIB, 1); + sigwait(&set, &signal); - memset(ptr, 2, MEM_SIZE_MIB); + clock_gettime(CLOCK_BOOTTIME, &start); + touch_memory(ptr, MEM_SIZE_MIB, 2); + clock_gettime(CLOCK_BOOTTIME, &end); + + duration_nanos = (end.tv_sec - start.tv_sec) * NANOS_PER_SEC + end.tv_nsec - start.tv_nsec; + + out_file = fopen("/tmp/fast_page_fault_helper.out", "w"); + if (out_file == NULL) { + perror("fopen"); + return 1; + } + + fprintf(out_file, "%ld", duration_nanos); + if (fclose(out_file)) { + perror("fclose"); + return 1; + } return 0; } \ No newline at end of file diff --git a/resources/rebuild.sh b/resources/rebuild.sh index 9f6850d0555..56afd1bdbac 100755 --- a/resources/rebuild.sh +++ b/resources/rebuild.sh @@ -20,25 +20,6 @@ function install_dependencies { apt install -y bc flex bison gcc make libelf-dev libssl-dev squashfs-tools busybox-static tree cpio curl patch docker.io } -function dir2ext4img { - # ext4 - # https://unix.stackexchange.com/questions/503211/how-can-an-image-file-be-created-for-a-directory - local DIR=$1 - local IMG=$2 - # Default size for the resulting rootfs image is 300M - local SIZE=${3:-300M} - local TMP_MNT=$(mktemp -d) - truncate -s "$SIZE" "$IMG" - mkfs.ext4 -F "$IMG" - mount "$IMG" "$TMP_MNT" - tar c -C $DIR . |tar x -C "$TMP_MNT" - # cleanup - # Use the -l flag for lazy unmounting since sometimes umount fails - # with "device busy" and simply calling `sync` doesn't help - umount -l "$TMP_MNT" - rmdir $TMP_MNT -} - function prepare_docker { nohup /usr/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 & @@ -67,7 +48,7 @@ function build_rootfs { cp -rvf overlay/* $rootfs - # curl -O https://cloud-images.ubuntu.com/minimal/releases/jammy/release/ubuntu-22.04-minimal-cloudimg-amd64-root.tar.xz + # curl -O https://cloud-images.ubuntu.com/minimal/releases/noble/release/ubuntu-24.04-minimal-cloudimg-amd64-root.tar.xz # # TBD use systemd-nspawn instead of Docker # sudo tar xaf ubuntu-22.04-minimal-cloudimg-amd64-root.tar.xz -C $rootfs @@ -89,26 +70,13 @@ EOF # TBD what abt /etc/hosts? echo | tee $rootfs/etc/resolv.conf - # Generate key for ssh access from host - if [ ! -s id_rsa ]; then - ssh-keygen -f id_rsa -N "" - fi - install -d -m 0600 "$rootfs/root/.ssh/" - cp id_rsa.pub "$rootfs/root/.ssh/authorized_keys" - id_rsa=$OUTPUT_DIR/$ROOTFS_NAME.id_rsa - cp id_rsa $id_rsa - - # -comp zstd but guest kernel does not support rootfs_img="$OUTPUT_DIR/$ROOTFS_NAME.squashfs" mv $rootfs/root/manifest $OUTPUT_DIR/$ROOTFS_NAME.manifest - mksquashfs $rootfs $rootfs_img -all-root -noappend - rootfs_ext4=$OUTPUT_DIR/$ROOTFS_NAME.ext4 - dir2ext4img $rootfs $rootfs_ext4 + mksquashfs $rootfs $rootfs_img -all-root -noappend -comp zstd rm -rf $rootfs for bin in fast_page_fault_helper fillmem init readmem; do rm $PWD/overlay/usr/local/bin/$bin done - rm -f id_rsa{,.pub} rm -f nohup.out } @@ -152,13 +120,7 @@ EOF } function clone_amazon_linux_repo { - [ -d linux ] || git clone https://github.com/amazonlinux/linux linux -} - -function apply_kernel_patches_for_ci { - for p in $PWD/guest_configs/patches/* ; do - patch -p2 < $p - done + [ -d linux ] || git clone --no-checkout --filter=tree:0 https://github.com/amazonlinux/linux } # prints the git tag corresponding to the newest and best matching the provided kernel version $1 @@ -183,7 +145,8 @@ function build_al_kernel { local KERNEL_VERSION=$(echo $KERNEL_CFG | grep -Po "microvm-kernel-ci-$ARCH-\K(\d+\.\d+)") pushd linux - make distclean + # fails immediately after clone because nothing is checked out + make distclean || true git checkout $(get_tag $KERNEL_VERSION) @@ -200,8 +163,9 @@ function build_al_kernel { echo "FATAL: Unsupported architecture!" exit 1 fi - cp "$KERNEL_CFG" .config - + # Concatenate all config files into one. olddefconfig will then resolve + # as needed. Later values override earlier ones. + cat "$@" >.config make olddefconfig make -j $(nproc) $target LATEST_VERSION=$(cat include/config/kernel.release) @@ -226,16 +190,30 @@ function prepare_and_build_rootfs { compile_and_install $BIN/devmemread.c $BIN/devmemread fi - build_rootfs ubuntu-22.04 jammy + build_rootfs ubuntu-24.04 noble build_initramfs } +function vmlinux_split_debuginfo { + VMLINUX="$1" + DEBUGINFO="$VMLINUX.debug" + VMLINUX_ORIG="$VMLINUX" + if [ $ARCH = "aarch64" ]; then + # in aarch64, the debug info is in vmlinux + VMLINUX_ORIG=linux/vmlinux + fi + objcopy --only-keep-debug $VMLINUX_ORIG $DEBUGINFO + objcopy --preserve-dates --strip-debug --add-gnu-debuglink=$DEBUGINFO $VMLINUX + # gdb does not support compressed files, but compress them because they are huge + gzip -v $DEBUGINFO +} + function build_al_kernels { if [[ $# = 0 ]]; then local KERNEL_VERSION="all" elif [[ $# -ne 1 ]]; then die "Too many arguments in '$(basename $0) kernels' command. Please use \`$0 help\` for help." - else + else KERNEL_VERSION=$1 if [[ "$KERNEL_VERSION" != @(5.10|5.10-no-acpi|6.1) ]]; then die "Unsupported kernel version: '$KERNEL_VERSION'. Please use \`$0 help\` for help." @@ -244,22 +222,31 @@ function build_al_kernels { clone_amazon_linux_repo - # Apply kernel patches on top of AL configuration - apply_kernel_patches_for_ci + CI_CONFIG="$PWD/guest_configs/ci.config" if [[ "$KERNEL_VERSION" == @(all|5.10) ]]; then - build_al_kernel $PWD/guest_configs/microvm-kernel-ci-$ARCH-5.10.config + build_al_kernel $PWD/guest_configs/microvm-kernel-ci-$ARCH-5.10.config "$CI_CONFIG" fi if [[ $ARCH == "x86_64" && "$KERNEL_VERSION" == @(all|5.10-no-acpi) ]]; then - build_al_kernel $PWD/guest_configs/microvm-kernel-ci-$ARCH-5.10-no-acpi.config + build_al_kernel $PWD/guest_configs/microvm-kernel-ci-$ARCH-5.10-no-acpi.config "$CI_CONFIG" fi if [[ "$KERNEL_VERSION" == @(all|6.1) ]]; then - build_al_kernel $PWD/guest_configs/microvm-kernel-ci-$ARCH-6.1.config 5.10 + build_al_kernel $PWD/guest_configs/microvm-kernel-ci-$ARCH-6.1.config "$CI_CONFIG" fi - # Undo kernel patches on top of AL configuration - git restore $PWD/guest_configs - rm -rf $PWD/guest_configs/*.orig + # Build debug kernels + FTRACE_CONFIG="$PWD/guest_configs/ftrace.config" + DEBUG_CONFIG="$PWD/guest_configs/debug.config" + OUTPUT_DIR=$OUTPUT_DIR/debug + mkdir -pv $OUTPUT_DIR + if [[ "$KERNEL_VERSION" == @(all|5.10) ]]; then + build_al_kernel "$PWD/guest_configs/microvm-kernel-ci-$ARCH-5.10.config" "$CI_CONFIG" "$FTRACE_CONFIG" "$DEBUG_CONFIG" + vmlinux_split_debuginfo $OUTPUT_DIR/vmlinux-5.10.* + fi + if [[ "$KERNEL_VERSION" == @(all|6.1) ]]; then + build_al_kernel "$PWD/guest_configs/microvm-kernel-ci-$ARCH-6.1.config" "$CI_CONFIG" "$FTRACE_CONFIG" "$DEBUG_CONFIG" + vmlinux_split_debuginfo $OUTPUT_DIR/vmlinux-6.1.* + fi } function print_help { @@ -267,25 +254,25 @@ function print_help { Firecracker CI artifacts build script Usage: $(basename $0) [] [] - + Available commands: - + all (default) Build CI rootfs and default guest kernels using configurations from resources/guest_configs. This will patch the guest configurations with all the patches under resources/guest_configs/patches. This is the default command, if no command is chosen. - + rootfs Builds only the CI rootfs. - + kernels [version] Builds our the currently supported CI kernels. - + version: Optionally choose a kernel version to build. Supported versions are: 5.10, 5.10-no-acpi or 6.1. - + help Displays the help message and exits. EOF @@ -310,7 +297,7 @@ function main { fi set -x - + install_dependencies # Create the directory in which we will store the kernels and rootfs diff --git a/resources/seccomp/aarch64-unknown-linux-musl.json b/resources/seccomp/aarch64-unknown-linux-musl.json index 48d94a0f050..db3abe1eced 100644 --- a/resources/seccomp/aarch64-unknown-linux-musl.json +++ b/resources/seccomp/aarch64-unknown-linux-musl.json @@ -42,10 +42,6 @@ { "syscall": "close" }, - { - "syscall": "uname", - "comment": "Used for getting the kernel version, for validating io_uring support" - }, { "syscall": "eventfd2", "comment": "Used for creating io_uring completion event, on drive patch" @@ -78,10 +74,6 @@ "syscall": "fstat", "comment": "Used for drive patching & rescanning, for reading the local timezone from /etc/localtime" }, - { - "syscall": "faccessat", - "comment": "Used by aws-lc-sys" - }, { "syscall": "ftruncate", "comment": "Used for snapshotting" @@ -448,6 +440,10 @@ { "syscall": "recvmsg", "comment": "Used by vhost-user frontend to read response from the backend" + }, + { + "syscall": "restart_syscall", + "comment": "automatically issued by the kernel when specific timing-related syscalls (e.g. nanosleep) get interrupted by SIGSTOP" } ] }, @@ -730,6 +726,10 @@ { "syscall": "sched_yield", "comment": "Used by the rust standard library in std::sync::mpmc. Firecracker uses mpsc channels from this module for inter-thread communication" + }, + { + "syscall": "restart_syscall", + "comment": "automatically issued by the kernel when specific timing-related syscalls (e.g. nanosleep) get interrupted by SIGSTOP" } ] }, @@ -970,42 +970,6 @@ } ] }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1074048665, - "comment": "KVM_SET_MP_STATE" - } - ] - }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 2151722655, - "comment": "KVM_GET_VCPU_EVENTS" - } - ] - }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1077980832, - "comment": "KVM_SET_VCPU_EVENTS" - } - ] - }, { "syscall": "ioctl", "args": [ @@ -1018,18 +982,6 @@ } ] }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1074835116, - "comment": "KVM_SET_ONE_REG" - } - ] - }, { "syscall": "ioctl", "args": [ @@ -1061,6 +1013,10 @@ { "syscall": "sendmsg", "comment": "Used by vhost-user frontend to communicate with the backend" + }, + { + "syscall": "restart_syscall", + "comment": "automatically issued by the kernel when specific timing-related syscalls (e.g. nanosleep) get interrupted by SIGSTOP" } ] } diff --git a/resources/seccomp/x86_64-unknown-linux-musl.json b/resources/seccomp/x86_64-unknown-linux-musl.json index 861b69c6b44..95ceca1b7ef 100644 --- a/resources/seccomp/x86_64-unknown-linux-musl.json +++ b/resources/seccomp/x86_64-unknown-linux-musl.json @@ -42,10 +42,6 @@ { "syscall": "close" }, - { - "syscall": "uname", - "comment": "Used for getting the kernel version, for validating io_uring support" - }, { "syscall": "eventfd2", "comment": "Used for creating io_uring completion event, on drive patch" @@ -111,8 +107,8 @@ "comment": "sigaltstack is used by Rust stdlib to remove alternative signal stack during thread teardown." }, { - "syscall": "getrandom", - "comment": "getrandom is used by aws-lc library which we consume in virtio-rng" + "syscall": "getrandom", + "comment": "getrandom is used by aws-lc library which we consume in virtio-rng" }, { "syscall": "accept4", @@ -214,7 +210,7 @@ }, { "syscall": "madvise", - "comment": "Used by the VirtIO balloon device and by musl for some customer workloads. It is also used by aws-lc during random number generation. They setup a memory page that mark with MADV_WIPEONFORK to be able to detect forks. They also call it with -1 to see if madvise is supported in certain platforms." + "comment": "Used by the VirtIO balloon device and by musl for some customer workloads. It is also used by aws-lc during random number generation. They setup a memory page that mark with MADV_WIPEONFORK to be able to detect forks. They also call it with -1 to see if madvise is supported in certain platforms." }, { "syscall": "mmap", @@ -456,6 +452,10 @@ { "syscall": "recvmsg", "comment": "Used by vhost-user frontend to read response from the backend" + }, + { + "syscall": "restart_syscall", + "comment": "automatically issued by the kernel when specific timing-related syscalls (e.g. nanosleep) get interrupted by SIGSTOP" } ] }, @@ -738,6 +738,10 @@ { "syscall": "sched_yield", "comment": "Used by the rust standard library in std::sync::mpmc. Firecracker uses mpsc channels from this module for inter-thread communication" + }, + { + "syscall": "restart_syscall", + "comment": "automatically issued by the kernel when specific timing-related syscalls (e.g. nanosleep) get interrupted by SIGSTOP" } ] }, @@ -978,18 +982,6 @@ } ] }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1074048665, - "comment": "KVM_SET_MP_STATE" - } - ] - }, { "syscall": "ioctl", "args": [ @@ -1002,18 +994,6 @@ } ] }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1077980832, - "comment": "KVM_SET_VCPU_EVENTS" - } - ] - }, { "syscall": "ioctl", "args": [ @@ -1038,78 +1018,6 @@ } ] }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1074310800, - "comment": "KVM_SET_CPUID2" - } - ] - }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1140895375, - "comment": "KVM_SET_LAPIC" - } - ] - }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1074310793, - "comment": "KVM_SET_MSRS" - } - ] - }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1083223682, - "comment": "KVM_SET_REGS" - } - ] - }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1094233732, - "comment": "KVM_SET_SREGS" - } - ] - }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 2154868383, - "comment": "KVM_GET_PIT2" - } - ] - }, { "syscall": "ioctl", "args": [ @@ -1158,18 +1066,6 @@ } ] }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1082175138, - "comment": "KVM_SET_DEBUGREGS" - } - ] - }, { "syscall": "ioctl", "args": [ @@ -1189,8 +1085,8 @@ "index": 1, "type": "dword", "op": "eq", - "val": 1342221989, - "comment": "KVM_SET_XSAVE" + "val": 2415963855, + "comment": "KVM_GET_XSAVE2" } ] }, @@ -1206,30 +1102,6 @@ } ] }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 1099476647, - "comment": "KVM_SET_XCRS" - } - ] - }, - { - "syscall": "ioctl", - "args": [ - { - "index": 1, - "type": "dword", - "op": "eq", - "val": 44706, - "comment": "KVM_SET_TSC_KHZ" - } - ] - }, { "syscall": "ioctl", "args": [ @@ -1273,6 +1145,10 @@ { "syscall": "sendmsg", "comment": "Used by vhost-user frontend to communicate with the backend" + }, + { + "syscall": "restart_syscall", + "comment": "automatically issued by the kernel when specific timing-related syscalls (e.g. nanosleep) get interrupted by SIGSTOP" } ] } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ff74857c81d..6bf302b6e1a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -11,7 +11,7 @@ # allowlisted using a toolchain that requires it, causing the A/B-test to # always fail. [toolchain] -channel = "1.79.0" +channel = "1.85.0" targets = ["x86_64-unknown-linux-musl", "aarch64-unknown-linux-musl"] profile = "minimal" diff --git a/src/acpi-tables/Cargo.toml b/src/acpi-tables/Cargo.toml index 4d066eb9338..141f09a47da 100644 --- a/src/acpi-tables/Cargo.toml +++ b/src/acpi-tables/Cargo.toml @@ -2,15 +2,15 @@ name = "acpi_tables" version = "0.1.0" authors = ["The Cloud Hypervisor Authors", "Amazon Firecracker team "] -edition = "2021" +edition = "2024" license = "Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] displaydoc = "0.2.5" -thiserror = "1.0.67" -vm-memory = { version = "0.16.0", features = ["backend-mmap", "backend-bitmap"] } -zerocopy = { version = "0.8.8", features = ["derive"] } +thiserror = "2.0.12" +vm-memory = { version = "0.16.1", features = ["backend-mmap", "backend-bitmap"] } +zerocopy = { version = "0.8.24", features = ["derive"] } [lib] bench = false diff --git a/src/acpi-tables/src/aml.rs b/src/acpi-tables/src/aml.rs index 8f839c258e0..5c7e91a2d6a 100644 --- a/src/acpi-tables/src/aml.rs +++ b/src/acpi-tables/src/aml.rs @@ -180,7 +180,7 @@ pub struct Package<'a> { children: Vec<&'a dyn Aml>, } -impl<'a> Aml for Package<'a> { +impl Aml for Package<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { let mut tmp = vec![self.children.len().try_into().unwrap()]; for child in &self.children { @@ -265,12 +265,12 @@ impl EisaName { let data = name.as_bytes(); - let value: u32 = (u32::from(data[0] - 0x40) << 26 - | u32::from(data[1] - 0x40) << 21 - | u32::from(data[2] - 0x40) << 16 - | name.chars().nth(3).unwrap().to_digit(16).unwrap() << 12 - | name.chars().nth(4).unwrap().to_digit(16).unwrap() << 8 - | name.chars().nth(5).unwrap().to_digit(16).unwrap() << 4 + let value: u32 = ((u32::from(data[0] - 0x40) << 26) + | (u32::from(data[1] - 0x40) << 21) + | (u32::from(data[2] - 0x40) << 16) + | (name.chars().nth(3).unwrap().to_digit(16).unwrap() << 12) + | (name.chars().nth(4).unwrap().to_digit(16).unwrap() << 8) + | (name.chars().nth(5).unwrap().to_digit(16).unwrap() << 4) | name.chars().nth(6).unwrap().to_digit(16).unwrap()) .swap_bytes(); @@ -336,7 +336,7 @@ pub struct ResourceTemplate<'a> { children: Vec<&'a dyn Aml>, } -impl<'a> Aml for ResourceTemplate<'a> { +impl Aml for ResourceTemplate<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { let mut tmp = Vec::new(); // Add buffer data @@ -439,7 +439,7 @@ where r#type: AddressSpaceType::Memory, min, max, - type_flags: (cacheable as u8) << 1 | u8::from(read_write), + type_flags: ((cacheable as u8) << 1) | u8::from(read_write), }) } @@ -471,7 +471,7 @@ where bytes.push(descriptor); // Word Address Space Descriptor bytes.extend_from_slice(&(TryInto::::try_into(length).unwrap()).to_le_bytes()); bytes.push(self.r#type as u8); // type - let generic_flags = 1 << 2 /* Min Fixed */ | 1 << 3; // Max Fixed + let generic_flags = (1 << 2) /* Min Fixed */ | (1 << 3); // Max Fixed bytes.push(generic_flags); bytes.push(self.type_flags); } @@ -591,9 +591,9 @@ impl Aml for Interrupt { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { bytes.push(0x89); // Extended IRQ Descriptor bytes.extend_from_slice(&6u16.to_le_bytes()); - let flags = u8::from(self.shared) << 3 - | u8::from(self.active_low) << 2 - | u8::from(self.edge_triggered) << 1 + let flags = (u8::from(self.shared) << 3) + | (u8::from(self.active_low) << 2) + | (u8::from(self.edge_triggered) << 1) | u8::from(self.consumer); bytes.push(flags); bytes.push(1u8); // count @@ -607,7 +607,7 @@ pub struct Device<'a> { children: Vec<&'a dyn Aml>, } -impl<'a> Aml for Device<'a> { +impl Aml for Device<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { let mut tmp = Vec::new(); self.path.append_aml_bytes(&mut tmp)?; @@ -637,7 +637,7 @@ pub struct Scope<'a> { children: Vec<&'a dyn Aml>, } -impl<'a> Aml for Scope<'a> { +impl Aml for Scope<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { let mut tmp = Vec::new(); self.path.append_aml_bytes(&mut tmp)?; @@ -678,11 +678,11 @@ impl<'a> Method<'a> { } } -impl<'a> Aml for Method<'a> { +impl Aml for Method<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { let mut tmp = Vec::new(); self.path.append_aml_bytes(&mut tmp)?; - let flags: u8 = (self.args & 0x7) | u8::from(self.serialized) << 3; + let flags: u8 = (self.args & 0x7) | (u8::from(self.serialized) << 3); tmp.push(flags); for child in &self.children { child.append_aml_bytes(&mut tmp)?; @@ -707,7 +707,7 @@ impl<'a> Return<'a> { } } -impl<'a> Aml for Return<'a> { +impl Aml for Return<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { bytes.push(0xa4); // ReturnOp self.value.append_aml_bytes(bytes)?; @@ -766,7 +766,7 @@ impl Aml for Field { let mut tmp = Vec::new(); self.path.append_aml_bytes(&mut tmp)?; - let flags: u8 = self.access_type as u8 | (self.update_rule as u8) << 5; + let flags: u8 = self.access_type as u8 | ((self.update_rule as u8) << 5); tmp.push(flags); for field in self.fields.iter() { @@ -850,7 +850,7 @@ impl<'a> If<'a> { } } -impl<'a> Aml for If<'a> { +impl Aml for If<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { let mut tmp = Vec::new(); self.predicate.append_aml_bytes(&mut tmp)?; @@ -878,7 +878,7 @@ impl<'a> Equal<'a> { } } -impl<'a> Aml for Equal<'a> { +impl Aml for Equal<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { bytes.push(0x93); // LEqualOp self.left.append_aml_bytes(bytes)?; @@ -898,7 +898,7 @@ impl<'a> LessThan<'a> { } } -impl<'a> Aml for LessThan<'a> { +impl Aml for LessThan<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { bytes.push(0x95); // LLessOp self.left.append_aml_bytes(bytes)?; @@ -942,7 +942,7 @@ impl<'a> Store<'a> { } } -impl<'a> Aml for Store<'a> { +impl Aml for Store<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { bytes.push(0x70); // StoreOp self.value.append_aml_bytes(bytes)?; @@ -1023,7 +1023,7 @@ impl<'a> Notify<'a> { } } -impl<'a> Aml for Notify<'a> { +impl Aml for Notify<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { bytes.push(0x86); // NotifyOp self.object.append_aml_bytes(bytes)?; @@ -1046,7 +1046,7 @@ impl<'a> While<'a> { } } -impl<'a> Aml for While<'a> { +impl Aml for While<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { let mut tmp = Vec::new(); self.predicate.append_aml_bytes(&mut tmp)?; @@ -1116,7 +1116,7 @@ impl<'a> MethodCall<'a> { } } -impl<'a> Aml for MethodCall<'a> { +impl Aml for MethodCall<'_> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { self.name.append_aml_bytes(bytes)?; for arg in self.args.iter() { @@ -1169,7 +1169,7 @@ impl<'a, T> CreateField<'a, T> { } } -impl<'a> Aml for CreateField<'a, u64> { +impl Aml for CreateField<'_, u64> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { bytes.push(0x8f); // CreateQWordFieldOp self.buffer.append_aml_bytes(bytes)?; @@ -1178,7 +1178,7 @@ impl<'a> Aml for CreateField<'a, u64> { } } -impl<'a> Aml for CreateField<'a, u32> { +impl Aml for CreateField<'_, u32> { fn append_aml_bytes(&self, bytes: &mut Vec) -> Result<(), AmlError> { bytes.push(0x8a); // CreateDWordFieldOp self.buffer.append_aml_bytes(bytes)?; @@ -1263,15 +1263,17 @@ mod tests { assert_eq!( Scope::new( "_SB_.MBRD".try_into().unwrap(), - vec![&Name::new( - "_CRS".try_into().unwrap(), - &ResourceTemplate::new(vec![&Memory32Fixed::new( - true, - 0xE800_0000, - 0x1000_0000 - )]) - ) - .unwrap()] + vec![ + &Name::new( + "_CRS".try_into().unwrap(), + &ResourceTemplate::new(vec![&Memory32Fixed::new( + true, + 0xE800_0000, + 0x1000_0000 + )]) + ) + .unwrap() + ] ) .to_aml_bytes() .unwrap(), @@ -1438,13 +1440,15 @@ mod tests { assert_eq!( Name::new( "_CRS".try_into().unwrap(), - &ResourceTemplate::new(vec![&AddressSpace::new_memory( - AddressSpaceCacheable::Cacheable, - true, - 0x8_0000_0000u64, - 0xf_ffff_ffffu64 - ) - .unwrap()]) + &ResourceTemplate::new(vec![ + &AddressSpace::new_memory( + AddressSpaceCacheable::Cacheable, + true, + 0x8_0000_0000u64, + 0xf_ffff_ffffu64 + ) + .unwrap() + ]) ) .unwrap() .to_aml_bytes() @@ -1491,12 +1495,12 @@ mod tests { assert_eq!(create_pkg_length(&[0u8; 62], true), vec![63]); assert_eq!( create_pkg_length(&[0u8; 64], true), - vec![1 << 6 | (66 & 0xf), 66 >> 4] + vec![(1 << 6) | (66 & 0xf), 66 >> 4] ); assert_eq!( create_pkg_length(&[0u8; 4096], true), vec![ - 2 << 6 | (4099 & 0xf) as u8, + (2 << 6) | (4099 & 0xf) as u8, ((4099 >> 4) & 0xff).try_into().unwrap(), ((4099 >> 12) & 0xff).try_into().unwrap() ] @@ -1553,7 +1557,9 @@ mod tests { (&"_SB_.PCI0._HID".try_into().unwrap() as &Path) .to_aml_bytes() .unwrap(), - [0x2F, 0x03, 0x5F, 0x53, 0x42, 0x5F, 0x50, 0x43, 0x49, 0x30, 0x5F, 0x48, 0x49, 0x44] + [ + 0x2F, 0x03, 0x5F, 0x53, 0x42, 0x5F, 0x50, 0x43, 0x49, 0x30, 0x5F, 0x48, 0x49, 0x44 + ] ); } @@ -2007,13 +2013,15 @@ mod tests { vec![ &Name::new( "MR64".try_into().unwrap(), - &ResourceTemplate::new(vec![&AddressSpace::new_memory( - AddressSpaceCacheable::Cacheable, - true, - 0x0000_0000_0000_0000u64, - 0xFFFF_FFFF_FFFF_FFFEu64 - ) - .unwrap()]) + &ResourceTemplate::new(vec![ + &AddressSpace::new_memory( + AddressSpaceCacheable::Cacheable, + true, + 0x0000_0000_0000_0000u64, + 0xFFFF_FFFF_FFFF_FFFEu64 + ) + .unwrap() + ]) ) .unwrap(), &CreateField::::new( diff --git a/src/acpi-tables/src/dsdt.rs b/src/acpi-tables/src/dsdt.rs index 49b05918937..dfb7c9b1b84 100644 --- a/src/acpi-tables/src/dsdt.rs +++ b/src/acpi-tables/src/dsdt.rs @@ -6,7 +6,7 @@ use std::mem::size_of; use vm_memory::{Address, Bytes, GuestAddress, GuestMemory}; use zerocopy::IntoBytes; -use crate::{checksum, AcpiError, Result, Sdt, SdtHeader}; +use crate::{AcpiError, Result, Sdt, SdtHeader, checksum}; /// Differentiated System Description Table (DSDT) /// diff --git a/src/acpi-tables/src/fadt.rs b/src/acpi-tables/src/fadt.rs index 5a3a8e5694b..c14f62bf230 100644 --- a/src/acpi-tables/src/fadt.rs +++ b/src/acpi-tables/src/fadt.rs @@ -7,7 +7,7 @@ use vm_memory::{Bytes, GuestAddress, GuestMemory}; use zerocopy::little_endian::{U16, U32, U64}; use zerocopy::{Immutable, IntoBytes}; -use crate::{checksum, GenericAddressStructure, Result, Sdt, SdtHeader}; +use crate::{GenericAddressStructure, Result, Sdt, SdtHeader, checksum}; #[cfg(target_arch = "x86_64")] pub const IAPC_BOOT_ARG_FLAGS_VGA_NOT_PRESENT: u16 = 2; @@ -41,7 +41,7 @@ pub const FADT_F_HW_REDUCED_ACPI: u8 = 20; /// the pointer to the DSDT table. /// More information about this table can be found in the ACPI specification: /// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#fixed-acpi-description-table-fadt -#[repr(packed)] +#[repr(C, packed)] #[derive(Debug, Copy, Clone, Default, IntoBytes, Immutable)] pub struct Fadt { header: SdtHeader, diff --git a/src/acpi-tables/src/lib.rs b/src/acpi-tables/src/lib.rs index 301a2d1cc95..321328047ed 100644 --- a/src/acpi-tables/src/lib.rs +++ b/src/acpi-tables/src/lib.rs @@ -49,7 +49,7 @@ pub enum AcpiError { pub type Result = std::result::Result; /// ACPI type representing memory addresses -#[repr(packed)] +#[repr(C, packed)] #[derive(IntoBytes, Immutable, Clone, Copy, Debug, Default)] pub struct GenericAddressStructure { pub address_space_id: u8, @@ -78,7 +78,7 @@ impl GenericAddressStructure { } /// Header included in all System Descriptor Tables -#[repr(packed)] +#[repr(C, packed)] #[derive(Clone, Debug, Copy, Default, IntoBytes, Immutable)] pub struct SdtHeader { pub signature: [u8; 4], diff --git a/src/acpi-tables/src/madt.rs b/src/acpi-tables/src/madt.rs index 28f5c108b40..eaef031e337 100644 --- a/src/acpi-tables/src/madt.rs +++ b/src/acpi-tables/src/madt.rs @@ -9,7 +9,7 @@ use vm_memory::{Address, Bytes, GuestAddress, GuestMemory}; use zerocopy::little_endian::U32; use zerocopy::{Immutable, IntoBytes}; -use crate::{checksum, AcpiError, Result, Sdt, SdtHeader}; +use crate::{AcpiError, Result, Sdt, SdtHeader, checksum}; const MADT_CPU_ENABLE_FLAG: u32 = 0; @@ -17,7 +17,7 @@ const MADT_CPU_ENABLE_FLAG: u32 = 0; // them as bytes in guest memory, so here we just ignore dead code to avoid having to name // everything with an underscore prefix #[allow(dead_code)] -#[repr(packed)] +#[repr(C, packed)] #[derive(Copy, Clone, Debug, Default, IntoBytes, Immutable)] pub struct LocalAPIC { r#type: u8, @@ -43,7 +43,7 @@ impl LocalAPIC { // them as bytes in guest memory, so here we just ignore dead code to avoid having to name // everything with an underscore prefix #[allow(dead_code)] -#[repr(packed)] +#[repr(C, packed)] #[derive(Copy, Clone, Debug, Default, IntoBytes, Immutable)] pub struct IoAPIC { r#type: u8, @@ -71,7 +71,7 @@ impl IoAPIC { // them as bytes in guest memory, so here we just ignore dead code to avoid having to name // everything with an underscore prefix #[allow(dead_code)] -#[repr(packed)] +#[repr(C, packed)] #[derive(Debug, IntoBytes, Immutable)] struct MadtHeader { sdt: SdtHeader, diff --git a/src/acpi-tables/src/rsdp.rs b/src/acpi-tables/src/rsdp.rs index aa413b3cdfb..06b3dfda4a3 100644 --- a/src/acpi-tables/src/rsdp.rs +++ b/src/acpi-tables/src/rsdp.rs @@ -8,7 +8,7 @@ use vm_memory::{Bytes, GuestAddress, GuestMemory}; use zerocopy::little_endian::{U32, U64}; use zerocopy::{Immutable, IntoBytes}; -use crate::{checksum, Result, Sdt}; +use crate::{Result, Sdt, checksum}; // clippy doesn't understand that we actually "use" the fields of this struct when we serialize // them as bytes in guest memory, so here we just ignore dead code to avoid having to name @@ -21,7 +21,7 @@ use crate::{checksum, Result, Sdt}; /// a pointer to XSDT /// More information about this structure can be found in the ACPI specification: /// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#root-system-description-pointer-rsdp -#[repr(packed)] +#[repr(C, packed)] #[derive(Clone, Copy, Debug, Default, IntoBytes, Immutable)] pub struct Rsdp { signature: [u8; 8], diff --git a/src/acpi-tables/src/xsdt.rs b/src/acpi-tables/src/xsdt.rs index ed6798c1137..a11cadc6f75 100644 --- a/src/acpi-tables/src/xsdt.rs +++ b/src/acpi-tables/src/xsdt.rs @@ -8,7 +8,7 @@ use std::mem::size_of; use vm_memory::{Address, Bytes, GuestAddress, GuestMemory}; use zerocopy::IntoBytes; -use crate::{checksum, AcpiError, Result, Sdt, SdtHeader}; +use crate::{AcpiError, Result, Sdt, SdtHeader, checksum}; /// Extended System Description Table (XSDT) /// diff --git a/src/clippy-tracing/Cargo.toml b/src/clippy-tracing/Cargo.toml index 4dcd08e42eb..5d2fac82c7b 100644 --- a/src/clippy-tracing/Cargo.toml +++ b/src/clippy-tracing/Cargo.toml @@ -2,7 +2,7 @@ name = "clippy-tracing" version = "0.1.0" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" license = "Apache-2.0" [[bin]] @@ -10,15 +10,15 @@ name = "clippy-tracing" bench = false [dependencies] -clap = { version = "4.5.20", features = ["derive"] } -itertools = "0.13.0" -proc-macro2 = { version = "1.0.89", features = ["span-locations"] } -quote = "1.0.37" -syn = { version = "2.0.85", features = ["full", "extra-traits", "visit", "visit-mut", "printing"] } +clap = { version = "4.5.35", features = ["derive"] } +itertools = "0.14.0" +proc-macro2 = { version = "1.0.94", features = ["span-locations"] } +quote = "1.0.40" +syn = { version = "2.0.100", features = ["full", "extra-traits", "visit", "visit-mut", "printing"] } walkdir = "2.5.0" [dev-dependencies] -uuid = { version = "1.11.0", features = ["v4"] } +uuid = { version = "1.16.0", features = ["v4"] } [lints] workspace = true diff --git a/src/clippy-tracing/src/main.rs b/src/clippy-tracing/src/main.rs index 61de2eee148..c89fb6a5d37 100644 --- a/src/clippy-tracing/src/main.rs +++ b/src/clippy-tracing/src/main.rs @@ -1,8 +1,6 @@ // Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -#![warn(clippy::pedantic)] - //! A tool to add, remove and check for `tracing::instrument` in large projects where it is //! infeasible to manually add it to thousands of functions. @@ -273,7 +271,7 @@ fn exec() -> Result, ExecError> { // The file must not be a `build.rs` file. let not_build_file = !entry_path.ends_with("build.rs"); // The file must be a `.rs` file. - let is_rs_file = entry_path.extension().map_or(false, |ext| ext == "rs"); + let is_rs_file = entry_path.extension().is_some_and(|ext| ext == "rs"); if no_excluded_strings && not_build_file && is_rs_file { let file = OpenOptions::new() diff --git a/src/clippy-tracing/tests/integration_tests.rs b/src/clippy-tracing/tests/integration_tests.rs index 1a7af70dc7c..b62f08a1f2a 100644 --- a/src/clippy-tracing/tests/integration_tests.rs +++ b/src/clippy-tracing/tests/integration_tests.rs @@ -1,7 +1,7 @@ // Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use std::fs::{remove_file, OpenOptions}; +use std::fs::{OpenOptions, remove_file}; use std::io::{Read, Write}; use std::process::Command; diff --git a/src/cpu-template-helper/Cargo.toml b/src/cpu-template-helper/Cargo.toml index a5f06010aed..61536349d3f 100644 --- a/src/cpu-template-helper/Cargo.toml +++ b/src/cpu-template-helper/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "cpu-template-helper" -version = "1.10.0-dev" +version = "1.12.0-dev" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" license = "Apache-2.0" [[bin]] @@ -10,13 +10,13 @@ name = "cpu-template-helper" bench = false [dependencies] -clap = { version = "4.5.20", features = ["derive", "string"] } +clap = { version = "4.5.35", features = ["derive", "string"] } displaydoc = "0.2.5" -libc = "0.2.161" +libc = "0.2.171" log-instrument = { path = "../log-instrument", optional = true } -serde = { version = "1.0.214", features = ["derive"] } -serde_json = "1.0.132" -thiserror = "1.0.67" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" +thiserror = "2.0.12" vmm = { path = "../vmm" } vmm-sys-util = "0.12.1" diff --git a/src/cpu-template-helper/src/main.rs b/src/cpu-template-helper/src/main.rs index 84a127e353a..35b7ea22d82 100644 --- a/src/cpu-template-helper/src/main.rs +++ b/src/cpu-template-helper/src/main.rs @@ -161,7 +161,7 @@ fn run(cli: Cli) -> Result<(), HelperError> { let (vmm, vm_resources) = utils::build_microvm_from_config(config, template)?; let cpu_template = vm_resources - .vm_config + .machine_config .cpu_template .get_cpu_template()? .into_owned(); diff --git a/src/cpu-template-helper/src/template/dump/aarch64.rs b/src/cpu-template-helper/src/template/dump/aarch64.rs index 8fd44ddcd14..3456298f521 100644 --- a/src/cpu-template-helper/src/template/dump/aarch64.rs +++ b/src/cpu-template-helper/src/template/dump/aarch64.rs @@ -1,7 +1,7 @@ // Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use vmm::arch::aarch64::regs::{RegSize, PC, SYS_CNTPCT_EL0, SYS_CNTV_CVAL_EL0}; +use vmm::arch::aarch64::regs::{PC, RegSize, SYS_CNTPCT_EL0, SYS_CNTV_CVAL_EL0}; use vmm::cpu_config::aarch64::custom_cpu_template::RegisterModifier; use vmm::cpu_config::templates::{CpuConfiguration, CustomCpuTemplate, RegisterValueFilter}; use vmm::logger::warn; @@ -50,7 +50,7 @@ const REG_EXCLUSION_LIST: [u64; 3] = [ #[cfg(test)] mod tests { - use vmm::arch::aarch64::regs::{reg_size, Aarch64RegisterRef, Aarch64RegisterVec}; + use vmm::arch::aarch64::regs::{Aarch64RegisterRef, Aarch64RegisterVec, reg_size}; use super::*; diff --git a/src/cpu-template-helper/src/template/dump/x86_64.rs b/src/cpu-template-helper/src/template/dump/x86_64.rs index 75eae9d0a15..eaa2553a658 100644 --- a/src/cpu-template-helper/src/template/dump/x86_64.rs +++ b/src/cpu-template-helper/src/template/dump/x86_64.rs @@ -3,7 +3,8 @@ use std::collections::BTreeMap; -use vmm::arch::x86_64::gen::msr_index::*; +use vmm::MSR_RANGE; +use vmm::arch::x86_64::generated::msr_index::*; use vmm::arch::x86_64::msr::MsrRange; use vmm::cpu_config::templates::{CpuConfiguration, CustomCpuTemplate, RegisterValueFilter}; use vmm::cpu_config::x86_64::cpuid::common::get_vendor_id_from_host; @@ -11,7 +12,6 @@ use vmm::cpu_config::x86_64::cpuid::{Cpuid, VENDOR_ID_AMD}; use vmm::cpu_config::x86_64::custom_cpu_template::{ CpuidLeafModifier, CpuidRegister, CpuidRegisterModifier, RegisterModifier, }; -use vmm::MSR_RANGE; use crate::utils::x86_64::{cpuid_leaf_modifier, cpuid_reg_modifier, msr_modifier}; diff --git a/src/cpu-template-helper/src/template/strip/aarch64.rs b/src/cpu-template-helper/src/template/strip/aarch64.rs index 3465bc919ba..d22217e6077 100644 --- a/src/cpu-template-helper/src/template/strip/aarch64.rs +++ b/src/cpu-template-helper/src/template/strip/aarch64.rs @@ -4,7 +4,7 @@ use vmm::cpu_config::aarch64::custom_cpu_template::RegisterModifier; use vmm::cpu_config::templates::CustomCpuTemplate; -use crate::template::strip::{strip_common, StripError}; +use crate::template::strip::{StripError, strip_common}; use crate::utils::aarch64::RegModifierMap; #[allow(dead_code)] diff --git a/src/cpu-template-helper/src/template/strip/mod.rs b/src/cpu-template-helper/src/template/strip/mod.rs index 1777d1d571b..81e6539e098 100644 --- a/src/cpu-template-helper/src/template/strip/mod.rs +++ b/src/cpu-template-helper/src/template/strip/mod.rs @@ -86,7 +86,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::utils::tests::{mock_modifier, MockModifierMapKey}; + use crate::utils::tests::{MockModifierMapKey, mock_modifier}; #[test] fn test_strip_common_with_single_input() { diff --git a/src/cpu-template-helper/src/template/strip/x86_64.rs b/src/cpu-template-helper/src/template/strip/x86_64.rs index a9db18fc15b..69c93d3df81 100644 --- a/src/cpu-template-helper/src/template/strip/x86_64.rs +++ b/src/cpu-template-helper/src/template/strip/x86_64.rs @@ -4,7 +4,7 @@ use vmm::cpu_config::templates::CustomCpuTemplate; use vmm::cpu_config::x86_64::custom_cpu_template::{CpuidLeafModifier, RegisterModifier}; -use crate::template::strip::{strip_common, StripError}; +use crate::template::strip::{StripError, strip_common}; use crate::utils::x86_64::{CpuidModifierMap, MsrModifierMap}; #[allow(dead_code)] diff --git a/src/cpu-template-helper/src/template/verify/aarch64.rs b/src/cpu-template-helper/src/template/verify/aarch64.rs index 19a76fadcb0..48d50f10de3 100644 --- a/src/cpu-template-helper/src/template/verify/aarch64.rs +++ b/src/cpu-template-helper/src/template/verify/aarch64.rs @@ -3,7 +3,7 @@ use vmm::cpu_config::templates::CustomCpuTemplate; -use super::{verify_common, VerifyError}; +use super::{VerifyError, verify_common}; use crate::utils::aarch64::RegModifierMap; pub fn verify( diff --git a/src/cpu-template-helper/src/template/verify/mod.rs b/src/cpu-template-helper/src/template/verify/mod.rs index e168d2816c4..1a83f6ba1b2 100644 --- a/src/cpu-template-helper/src/template/verify/mod.rs +++ b/src/cpu-template-helper/src/template/verify/mod.rs @@ -62,7 +62,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::utils::tests::{mock_modifier, MockModifierMapKey}; + use crate::utils::tests::{MockModifierMapKey, mock_modifier}; #[test] fn test_verify_modifier_map_with_non_existing_key() { diff --git a/src/cpu-template-helper/src/template/verify/x86_64.rs b/src/cpu-template-helper/src/template/verify/x86_64.rs index 9885b16898f..12aa4f3a0cc 100644 --- a/src/cpu-template-helper/src/template/verify/x86_64.rs +++ b/src/cpu-template-helper/src/template/verify/x86_64.rs @@ -3,7 +3,7 @@ use vmm::cpu_config::templates::CustomCpuTemplate; -use super::{verify_common, VerifyError}; +use super::{VerifyError, verify_common}; use crate::utils::x86_64::{CpuidModifierMap, MsrModifierMap}; pub fn verify( @@ -34,8 +34,8 @@ mod tests { use super::*; use crate::utils::x86_64::{ - cpuid_leaf_modifier, cpuid_reg_modifier, msr_modifier, CpuidModifierMapKey, - MsrModifierMapKey, + CpuidModifierMapKey, MsrModifierMapKey, cpuid_leaf_modifier, cpuid_reg_modifier, + msr_modifier, }; macro_rules! cpuid_modifier_map { diff --git a/src/cpu-template-helper/src/utils/mod.rs b/src/cpu-template-helper/src/utils/mod.rs index bd570840fc5..f23871df1a9 100644 --- a/src/cpu-template-helper/src/utils/mod.rs +++ b/src/cpu-template-helper/src/utils/mod.rs @@ -9,12 +9,12 @@ use std::io::Write; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; -use vmm::builder::{build_microvm_for_boot, StartMicrovmError}; +use vmm::builder::{StartMicrovmError, build_microvm_for_boot}; use vmm::cpu_config::templates::{CustomCpuTemplate, Numeric}; use vmm::resources::VmResources; -use vmm::seccomp_filters::get_empty_filters; +use vmm::seccomp::get_empty_filters; use vmm::vmm_config::instance_info::{InstanceInfo, VmState}; -use vmm::{EventManager, Vmm, HTTP_MAX_PAYLOAD_SIZE}; +use vmm::{EventManager, HTTP_MAX_PAYLOAD_SIZE, Vmm}; use vmm_sys_util::tempfile::TempFile; #[cfg(target_arch = "aarch64")] diff --git a/src/firecracker/Cargo.toml b/src/firecracker/Cargo.toml index 28aeda87a8c..17e1d8fcf2b 100644 --- a/src/firecracker/Cargo.toml +++ b/src/firecracker/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "firecracker" -version = "1.10.0-dev" +version = "1.12.0-dev" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" build = "build.rs" description = "Firecracker enables you to deploy workloads in lightweight virtual machines, called microVMs, which provide enhanced security and workload isolation over traditional VMs, while enabling the speed and resource efficiency of containers." homepage = "https://firecracker-microvm.github.io/" @@ -18,37 +18,35 @@ bench = false [dependencies] displaydoc = "0.2.5" event-manager = "0.4.0" -libc = "0.2.161" +libc = "0.2.171" log-instrument = { path = "../log-instrument", optional = true } micro_http = { git = "https://github.com/firecracker-microvm/micro-http" } -seccompiler = { path = "../seccompiler" } -serde = { version = "1.0.214", features = ["derive"] } +serde = { version = "1.0.219", features = ["derive"] } serde_derive = "1.0.136" -serde_json = "1.0.132" -thiserror = "1.0.67" +serde_json = "1.0.140" +thiserror = "2.0.12" timerfd = "1.6.0" utils = { path = "../utils" } vmm = { path = "../vmm" } vmm-sys-util = { version = "0.12.1", features = ["with-serde"] } [dev-dependencies] -cargo_toml = "0.20.5" -libc = "0.2.161" +cargo_toml = "0.22.1" +libc = "0.2.171" regex = { version = "1.11.1", default-features = false, features = ["std", "unicode-perl"] } # Dev-Dependencies for uffd examples -serde = { version = "1.0.214", features = ["derive"] } +serde = { version = "1.0.219", features = ["derive"] } userfaultfd = "0.8.1" [build-dependencies] -bincode = "1.2.1" seccompiler = { path = "../seccompiler" } -serde = { version = "1.0.214" } -serde_json = "1.0.132" +serde = { version = "1.0.219" } +serde_json = "1.0.140" [features] -tracing = ["log-instrument", "seccompiler/tracing", "utils/tracing", "vmm/tracing"] +tracing = ["log-instrument", "utils/tracing", "vmm/tracing"] gdb = ["vmm/gdb"] [lints] @@ -59,8 +57,8 @@ name = "uffd_malicious_handler" path = "examples/uffd/malicious_handler.rs" [[example]] -name = "uffd_valid_handler" -path = "examples/uffd/valid_handler.rs" +name = "uffd_on_demand_handler" +path = "examples/uffd/on_demand_handler.rs" [[example]] name = "uffd_fault_all_handler" diff --git a/src/firecracker/build.rs b/src/firecracker/build.rs index b20e1cd4e1e..595b1e0c00a 100644 --- a/src/firecracker/build.rs +++ b/src/firecracker/build.rs @@ -1,13 +1,8 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use std::collections::BTreeMap; -use std::fs::File; use std::path::Path; -use seccompiler::common::BpfProgram; -use seccompiler::compiler::{Compiler, JsonFile}; - const ADVANCED_BINARY_FILTER_FILE_NAME: &str = "seccomp_filter.bpf"; const JSON_DIR: &str = "../../resources/seccomp"; @@ -19,23 +14,34 @@ const SECCOMPILER_SRC_DIR: &str = "../seccompiler/src"; fn main() { // Target triple let target = std::env::var("TARGET").expect("Missing target."); + let debug: bool = std::env::var("DEBUG") + .expect("Missing debug.") + .parse() + .expect("Invalid env variable DEBUG"); let out_dir = std::env::var("OUT_DIR").expect("Missing build-level OUT_DIR."); // Target arch (x86_64 / aarch64) let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").expect("Missing target arch."); let seccomp_json_path = format!("{}/{}.json", JSON_DIR, target); - // If the current target doesn't have a default filter, use a default, empty filter. + // If the current target doesn't have a default filter, or if we're building a debug binary, + // use a default, empty filter. // This is to make sure that Firecracker builds even with libc toolchains for which we don't // provide a default filter. For example, GNU libc. - let seccomp_json_path = if Path::new(&seccomp_json_path).exists() { - seccomp_json_path - } else { + let seccomp_json_path = if debug { + println!( + "cargo:warning=Using empty default seccomp policy for debug builds: \ + `resources/seccomp/unimplemented.json`." + ); + format!("{}/unimplemented.json", JSON_DIR) + } else if !Path::new(&seccomp_json_path).exists() { println!( "cargo:warning=No default seccomp policy for target: {}. Defaulting to \ `resources/seccomp/unimplemented.json`.", target ); format!("{}/unimplemented.json", JSON_DIR) + } else { + seccomp_json_path }; // Retrigger the build script if the JSON file has changed. @@ -44,19 +50,7 @@ fn main() { // Also retrigger the build script on any seccompiler source code change. println!("cargo:rerun-if-changed={}", SECCOMPILER_SRC_DIR); - let input = std::fs::read_to_string(seccomp_json_path).expect("Correct input file"); - let filters: JsonFile = serde_json::from_str(&input).expect("Input read"); - - let arch = target_arch.as_str().try_into().expect("Target"); - let compiler = Compiler::new(arch); - - // transform the IR into a Map of BPFPrograms - let bpf_data: BTreeMap = compiler - .compile_blob(filters.0, false) - .expect("Successfull compilation"); - - // serialize the BPF programs & output them to a file let out_path = format!("{}/{}", out_dir, ADVANCED_BINARY_FILTER_FILE_NAME); - let output_file = File::create(out_path).expect("Create seccompiler output path"); - bincode::serialize_into(output_file, &bpf_data).expect("Seccompiler serialization"); + seccompiler::compile_bpf(&seccomp_json_path, &target_arch, &out_path, false) + .expect("Cannot compile seccomp filters"); } diff --git a/src/firecracker/examples/seccomp/jailer.rs b/src/firecracker/examples/seccomp/jailer.rs index f82e3f5e249..53822d377fa 100644 --- a/src/firecracker/examples/seccomp/jailer.rs +++ b/src/firecracker/examples/seccomp/jailer.rs @@ -5,7 +5,7 @@ use std::fs::File; use std::os::unix::process::CommandExt; use std::process::{Command, Stdio}; -use seccompiler::{apply_filter, deserialize_binary}; +use vmm::seccomp::{apply_filter, deserialize_binary}; fn main() { let args: Vec = args().collect(); @@ -13,12 +13,12 @@ fn main() { let bpf_path = &args[2]; let filter_file = File::open(bpf_path).unwrap(); - let map = deserialize_binary(&filter_file, None).unwrap(); + let map = deserialize_binary(&filter_file).unwrap(); // Loads filters. apply_filter(map.get("main").unwrap()).unwrap(); - Command::new(exec_file) + let _ = Command::new(exec_file) .stdin(Stdio::inherit()) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) diff --git a/src/firecracker/examples/seccomp/panic.rs b/src/firecracker/examples/seccomp/panic.rs index 7998552a4d1..db0446cf882 100644 --- a/src/firecracker/examples/seccomp/panic.rs +++ b/src/firecracker/examples/seccomp/panic.rs @@ -3,7 +3,7 @@ use std::env::args; use std::fs::File; -use seccompiler::{apply_filter, deserialize_binary}; +use vmm::seccomp::{apply_filter, deserialize_binary}; fn main() { let args: Vec = args().collect(); @@ -11,7 +11,7 @@ fn main() { let filter_thread = &args[2]; let filter_file = File::open(bpf_path).unwrap(); - let map = deserialize_binary(&filter_file, None).unwrap(); + let map = deserialize_binary(&filter_file).unwrap(); apply_filter(map.get(filter_thread).unwrap()).unwrap(); panic!("Expected panic."); } diff --git a/src/firecracker/examples/uffd/fault_all_handler.rs b/src/firecracker/examples/uffd/fault_all_handler.rs index 31ce68a97bc..ca7601ebf25 100644 --- a/src/firecracker/examples/uffd/fault_all_handler.rs +++ b/src/firecracker/examples/uffd/fault_all_handler.rs @@ -11,6 +11,7 @@ use std::fs::File; use std::os::unix::net::UnixListener; use uffd_utils::{Runtime, UffdHandler}; +use utils::time::{ClockType, get_time_us}; fn main() { let mut args = std::env::args(); @@ -24,6 +25,7 @@ fn main() { let (stream, _) = listener.accept().expect("Cannot listen on UDS socket"); let mut runtime = Runtime::new(stream, file); + runtime.install_panic_hook(); runtime.run(|uffd_handler: &mut UffdHandler| { // Read an event from the userfaultfd. let event = uffd_handler @@ -33,10 +35,13 @@ fn main() { match event { userfaultfd::Event::Pagefault { .. } => { + let start = get_time_us(ClockType::Monotonic); for region in uffd_handler.mem_regions.clone() { - uffd_handler - .serve_pf(region.mapping.base_host_virt_addr as _, region.mapping.size) + uffd_handler.serve_pf(region.base_host_virt_addr as _, region.size); } + let end = get_time_us(ClockType::Monotonic); + + println!("Finished Faulting All: {}us", end - start); } _ => panic!("Unexpected event on userfaultfd"), } diff --git a/src/firecracker/examples/uffd/on_demand_handler.rs b/src/firecracker/examples/uffd/on_demand_handler.rs new file mode 100644 index 00000000000..3be958b3578 --- /dev/null +++ b/src/firecracker/examples/uffd/on_demand_handler.rs @@ -0,0 +1,105 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Provides functionality for a userspace page fault handler +//! which loads the whole region from the backing memory file +//! when a page fault occurs. + +mod uffd_utils; + +use std::fs::File; +use std::os::unix::net::UnixListener; + +use uffd_utils::{Runtime, UffdHandler}; + +fn main() { + let mut args = std::env::args(); + let uffd_sock_path = args.nth(1).expect("No socket path given"); + let mem_file_path = args.next().expect("No memory file given"); + + let file = File::open(mem_file_path).expect("Cannot open memfile"); + + // Get Uffd from UDS. We'll use the uffd to handle PFs for Firecracker. + let listener = UnixListener::bind(uffd_sock_path).expect("Cannot bind to socket path"); + let (stream, _) = listener.accept().expect("Cannot listen on UDS socket"); + + let mut runtime = Runtime::new(stream, file); + runtime.install_panic_hook(); + runtime.run(|uffd_handler: &mut UffdHandler| { + // !DISCLAIMER! + // When using UFFD together with the balloon device, this handler needs to deal with + // `remove` and `pagefault` events. There are multiple things to keep in mind in + // such setups: + // + // As long as any `remove` event is pending in the UFFD queue, all ioctls return EAGAIN + // ----------------------------------------------------------------------------------- + // + // This means we cannot process UFFD events simply one-by-one anymore - if a `remove` event + // arrives, we need to pre-fetch all other events up to the `remove` event, to unblock the + // UFFD, and then go back to the process the pre-fetched events. + // + // UFFD might receive events in not in their causal order + // ----------------------------------------------------- + // + // For example, the guest + // kernel might first respond to a balloon inflation by freeing some memory, and + // telling Firecracker about this. Firecracker will then madvise(MADV_DONTNEED) the + // free memory range, which causes a `remove` event to be sent to UFFD. Then, the + // guest kernel might immediately fault the page in again (for example because + // default_on_oom was set). which causes a `pagefault` event to be sent to UFFD. + // + // However, the pagefault will be triggered from inside KVM on the vCPU thread, while the + // balloon device is handled by Firecracker on its VMM thread. This means that potentially + // this handler can receive the `pagefault` _before_ the `remove` event. + // + // This means that the simple "greedy" strategy of simply prefetching _all_ UFFD events + // to make sure no `remove` event is blocking us can result in the handler acting on + // the `pagefault` event before the `remove` message (despite the `remove` event being + // in the causal past of the `pagefault` event), which means that we will fault in a page + // from the snapshot file, while really we should be faulting in a zero page. + // + // In this example handler, we ignore this problem, to avoid + // complexity (under the assumption that the guest kernel will zero a newly faulted in + // page anyway). A production handler will most likely want to ensure that `remove` + // events for a specific range are always handled before `pagefault` events. + // + // Lastly, we still need to deal with the race condition where a `remove` event arrives + // in the UFFD queue after we got done reading all events, in which case we need to go + // back to reading more events before we can continue processing `pagefault`s. + let mut deferred_events = Vec::new(); + + loop { + // First, try events that we couldn't handle last round + let mut events_to_handle = Vec::from_iter(deferred_events.drain(..)); + + // Read all events from the userfaultfd. + while let Some(event) = uffd_handler.read_event().expect("Failed to read uffd_msg") { + events_to_handle.push(event); + } + + for event in events_to_handle.drain(..) { + // We expect to receive either a Page Fault or `remove` + // event (if the balloon device is enabled). + match event { + userfaultfd::Event::Pagefault { addr, .. } => { + if !uffd_handler.serve_pf(addr.cast(), uffd_handler.page_size) { + deferred_events.push(event); + } + } + userfaultfd::Event::Remove { start, end } => { + uffd_handler.mark_range_removed(start as u64, end as u64) + } + _ => panic!("Unexpected event on userfaultfd"), + } + } + + // We assume that really only the above removed/pagefault interaction can result in + // deferred events. In that scenario, the loop will always terminate (unless + // newly arriving `remove` events end up indefinitely blocking it, but there's nothing + // we can do about that, and it's a largely theoretical problem). + if deferred_events.is_empty() { + break; + } + } + }); +} diff --git a/src/firecracker/examples/uffd/uffd_utils.rs b/src/firecracker/examples/uffd/uffd_utils.rs index 52d33765bd8..07ed48f9439 100644 --- a/src/firecracker/examples/uffd/uffd_utils.rs +++ b/src/firecracker/examples/uffd/uffd_utils.rs @@ -4,11 +4,13 @@ // Not everything is used by both binaries #![allow(dead_code)] -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; +use std::ffi::c_void; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; use std::os::unix::net::UnixStream; use std::ptr; +use std::time::Duration; use serde::{Deserialize, Serialize}; use userfaultfd::{Error, Event, Uffd}; @@ -31,47 +33,81 @@ pub struct GuestRegionUffdMapping { /// Offset in the backend file/buffer where the region contents are. pub offset: u64, /// The configured page size for this memory region. - pub page_size_kib: usize, -} - -#[derive(Debug, Clone, Copy)] -pub enum MemPageState { - Uninitialized, - FromFile, - Removed, - Anonymous, + pub page_size: usize, } -#[derive(Debug, Clone)] -pub struct MemRegion { - pub mapping: GuestRegionUffdMapping, - page_states: HashMap, +impl GuestRegionUffdMapping { + fn contains(&self, fault_page_addr: u64) -> bool { + fault_page_addr >= self.base_host_virt_addr + && fault_page_addr < self.base_host_virt_addr + self.size as u64 + } } #[derive(Debug)] pub struct UffdHandler { - pub mem_regions: Vec, + pub mem_regions: Vec, pub page_size: usize, backing_buffer: *const u8, uffd: Uffd, + removed_pages: HashSet, } impl UffdHandler { - pub fn from_unix_stream(stream: &UnixStream, backing_buffer: *const u8, size: usize) -> Self { + fn try_get_mappings_and_file( + stream: &UnixStream, + ) -> Result<(String, Option), std::io::Error> { let mut message_buf = vec![0u8; 1024]; - let (bytes_read, file) = stream - .recv_with_fd(&mut message_buf[..]) - .expect("Cannot recv_with_fd"); + let (bytes_read, file) = stream.recv_with_fd(&mut message_buf[..])?; message_buf.resize(bytes_read, 0); - let body = String::from_utf8(message_buf).unwrap(); - let file = file.expect("Uffd not passed through UDS!"); + // We do not expect to receive non-UTF-8 data from Firecracker, so this is probably + // an error we can't recover from. Just immediately abort + let body = String::from_utf8(message_buf.clone()).unwrap_or_else(|_| { + panic!( + "Received body is not a utf-8 valid string. Raw bytes received: {message_buf:#?}" + ) + }); + Ok((body, file)) + } + + fn get_mappings_and_file(stream: &UnixStream) -> (String, File) { + // Sometimes, reading from the stream succeeds but we don't receive any + // UFFD descriptor. We don't really have a good understanding why this is + // happening, but let's try to be a bit more robust and retry a few times + // before we declare defeat. + for _ in 1..=5 { + match Self::try_get_mappings_and_file(stream) { + Ok((body, Some(file))) => { + return (body, file); + } + Ok((body, None)) => { + println!("Didn't receive UFFD over socket. We received: '{body}'. Retrying..."); + } + Err(err) => { + println!("Could not get UFFD and mapping from Firecracker: {err}. Retrying..."); + } + } + std::thread::sleep(Duration::from_millis(100)); + } + + panic!("Could not get UFFD and mappings after 5 retries"); + } - let mappings = serde_json::from_str::>(&body) - .expect("Cannot deserialize memory mappings."); + pub fn from_unix_stream(stream: &UnixStream, backing_buffer: *const u8, size: usize) -> Self { + let (body, file) = Self::get_mappings_and_file(stream); + let mappings = + serde_json::from_str::>(&body).unwrap_or_else(|_| { + panic!("Cannot deserialize memory mappings. Received body: {body}") + }); let memsize: usize = mappings.iter().map(|r| r.size).sum(); // Page size is the same for all memory regions, so just grab the first one - let page_size = mappings.first().unwrap().page_size_kib; + let first_mapping = mappings.first().unwrap_or_else(|| { + panic!( + "Cannot get the first mapping. Mappings size is {}. Received body: {body}", + mappings.len() + ) + }); + let page_size = first_mapping.page_size; // Make sure memory size matches backing data size. assert_eq!(memsize, size); @@ -79,13 +115,12 @@ impl UffdHandler { let uffd = unsafe { Uffd::from_raw_fd(file.into_raw_fd()) }; - let mem_regions = create_mem_regions(&mappings, page_size); - Self { - mem_regions, + mem_regions: mappings, page_size, backing_buffer, uffd, + removed_pages: HashSet::new(), } } @@ -93,43 +128,29 @@ impl UffdHandler { self.uffd.read_event() } - pub fn update_mem_state_mappings(&mut self, start: u64, end: u64, state: MemPageState) { - for region in self.mem_regions.iter_mut() { - for (key, value) in region.page_states.iter_mut() { - if key >= &start && key < &end { - *value = state; - } - } + pub fn mark_range_removed(&mut self, start: u64, end: u64) { + let pfn_start = start / self.page_size as u64; + let pfn_end = end / self.page_size as u64; + + for pfn in pfn_start..pfn_end { + self.removed_pages.insert(pfn); } } - pub fn serve_pf(&mut self, addr: *mut u8, len: usize) { + pub fn serve_pf(&mut self, addr: *mut u8, len: usize) -> bool { // Find the start of the page that the current faulting address belongs to. let dst = (addr as usize & !(self.page_size - 1)) as *mut libc::c_void; let fault_page_addr = dst as u64; - - // Get the state of the current faulting page. - for region in self.mem_regions.iter() { - match region.page_states.get(&fault_page_addr) { - // Our simple PF handler has a simple strategy: - // There exist 4 states in which a memory page can be in: - // 1. Uninitialized - page was never touched - // 2. FromFile - the page is populated with content from snapshotted memory file - // 3. Removed - MADV_DONTNEED was called due to balloon inflation - // 4. Anonymous - page was zeroed out -> this implies that more than one page fault - // event was received. This can be a consequence of guest reclaiming back its - // memory from the host (through balloon device) - Some(MemPageState::Uninitialized) | Some(MemPageState::FromFile) => { - let (start, end) = self.populate_from_file(region, fault_page_addr, len); - self.update_mem_state_mappings(start, end, MemPageState::FromFile); - return; - } - Some(MemPageState::Removed) | Some(MemPageState::Anonymous) => { - let (start, end) = self.zero_out(fault_page_addr); - self.update_mem_state_mappings(start, end, MemPageState::Anonymous); - return; + let fault_pfn = fault_page_addr / self.page_size as u64; + + if self.removed_pages.contains(&fault_pfn) { + self.zero_out(fault_page_addr); + return true; + } else { + for region in self.mem_regions.iter() { + if region.contains(fault_page_addr) { + return self.populate_from_file(region, fault_page_addr, len); } - None => {} } } @@ -139,23 +160,37 @@ impl UffdHandler { ); } - fn populate_from_file(&self, region: &MemRegion, dst: u64, len: usize) -> (u64, u64) { - let offset = dst - region.mapping.base_host_virt_addr; - let src = self.backing_buffer as u64 + region.mapping.offset + offset; + fn populate_from_file(&self, region: &GuestRegionUffdMapping, dst: u64, len: usize) -> bool { + let offset = dst - region.base_host_virt_addr; + let src = self.backing_buffer as u64 + region.offset + offset; - let ret = unsafe { - self.uffd - .copy(src as *const _, dst as *mut _, len, true) - .expect("Uffd copy failed") + unsafe { + match self.uffd.copy(src as *const _, dst as *mut _, len, true) { + // Make sure the UFFD copied some bytes. + Ok(value) => assert!(value > 0), + // Catch EAGAIN errors, which occur when a `remove` event lands in the UFFD + // queue while we're processing `pagefault` events. + // The weird cast is because the `bytes_copied` field is based on the + // `uffdio_copy->copy` field, which is a signed 64 bit integer, and if something + // goes wrong, it gets set to a -errno code. However, uffd-rs always casts this + // value to an unsigned `usize`, which scrambled the errno. + Err(Error::PartiallyCopied(bytes_copied)) + if bytes_copied == 0 || bytes_copied == (-libc::EAGAIN) as usize => + { + return false; + } + Err(Error::CopyFailed(errno)) + if std::io::Error::from(errno).raw_os_error().unwrap() == libc::EEXIST => {} + Err(e) => { + panic!("Uffd copy failed: {e:?}"); + } + } }; - // Make sure the UFFD copied some bytes. - assert!(ret > 0); - - (dst, dst + len as u64) + true } - fn zero_out(&mut self, addr: u64) -> (u64, u64) { + fn zero_out(&mut self, addr: u64) { let ret = unsafe { self.uffd .zeropage(addr as *mut _, self.page_size, true) @@ -163,8 +198,6 @@ impl UffdHandler { }; // Make sure the UFFD zeroed out some bytes. assert!(ret > 0); - - (addr, addr + self.page_size as u64) } } @@ -208,6 +241,43 @@ impl Runtime { } } + fn peer_process_credentials(&self) -> libc::ucred { + let mut creds: libc::ucred = libc::ucred { + pid: 0, + gid: 0, + uid: 0, + }; + let mut creds_size = size_of::() as u32; + let ret = unsafe { + libc::getsockopt( + self.stream.as_raw_fd(), + libc::SOL_SOCKET, + libc::SO_PEERCRED, + (&raw mut creds).cast::(), + &raw mut creds_size, + ) + }; + if ret != 0 { + panic!("Failed to get peer process credentials"); + } + creds + } + + pub fn install_panic_hook(&self) { + let peer_creds = self.peer_process_credentials(); + + let default_panic_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + let r = unsafe { libc::kill(peer_creds.pid, libc::SIGKILL) }; + + if r != 0 { + eprintln!("Failed to kill Firecracker process from panic hook"); + } + + default_panic_hook(panic_info); + })); + } + /// Polls the `UnixStream` and UFFD fds in a loop. /// When stream is polled, new uffd is retrieved. /// When uffd is polled, page fault is handled by @@ -272,28 +342,6 @@ impl Runtime { } } -fn create_mem_regions(mappings: &Vec, page_size: usize) -> Vec { - let mut mem_regions: Vec = Vec::with_capacity(mappings.len()); - - for r in mappings.iter() { - let mapping = r.clone(); - let mut addr = r.base_host_virt_addr; - let end_addr = r.base_host_virt_addr + r.size as u64; - let mut page_states = HashMap::new(); - - while addr < end_addr { - page_states.insert(addr, MemPageState::Uninitialized); - addr += page_size as u64; - } - mem_regions.push(MemRegion { - mapping, - page_states, - }); - } - - mem_regions -} - #[cfg(test)] mod tests { use std::mem::MaybeUninit; @@ -341,7 +389,7 @@ mod tests { base_host_virt_addr: 0, size: 0x1000, offset: 0, - page_size_kib: 4096, + page_size: 4096, }]; let dummy_memory_region_json = serde_json::to_string(&dummy_memory_region).unwrap(); @@ -374,7 +422,7 @@ mod tests { base_host_virt_addr: 0, size: 0, offset: 0, - page_size_kib: 4096, + page_size: 4096, }]; let error_memory_region_json = serde_json::to_string(&error_memory_region).unwrap(); stream diff --git a/src/firecracker/examples/uffd/valid_handler.rs b/src/firecracker/examples/uffd/valid_handler.rs deleted file mode 100644 index cfc5faf432c..00000000000 --- a/src/firecracker/examples/uffd/valid_handler.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -//! Provides functionality for a userspace page fault handler -//! which loads the whole region from the backing memory file -//! when a page fault occurs. - -mod uffd_utils; - -use std::fs::File; -use std::os::unix::net::UnixListener; - -use uffd_utils::{MemPageState, Runtime, UffdHandler}; - -fn main() { - let mut args = std::env::args(); - let uffd_sock_path = args.nth(1).expect("No socket path given"); - let mem_file_path = args.next().expect("No memory file given"); - - let file = File::open(mem_file_path).expect("Cannot open memfile"); - - // Get Uffd from UDS. We'll use the uffd to handle PFs for Firecracker. - let listener = UnixListener::bind(uffd_sock_path).expect("Cannot bind to socket path"); - let (stream, _) = listener.accept().expect("Cannot listen on UDS socket"); - - let mut runtime = Runtime::new(stream, file); - runtime.run(|uffd_handler: &mut UffdHandler| { - // Read an event from the userfaultfd. - let event = uffd_handler - .read_event() - .expect("Failed to read uffd_msg") - .expect("uffd_msg not ready"); - - // We expect to receive either a Page Fault or Removed - // event (if the balloon device is enabled). - match event { - userfaultfd::Event::Pagefault { addr, .. } => { - uffd_handler.serve_pf(addr.cast(), uffd_handler.page_size) - } - userfaultfd::Event::Remove { start, end } => uffd_handler.update_mem_state_mappings( - start as u64, - end as u64, - MemPageState::Removed, - ), - _ => panic!("Unexpected event on userfaultfd"), - } - }); -} diff --git a/src/firecracker/src/api_server/mod.rs b/src/firecracker/src/api_server/mod.rs index 6ac2955af8f..3dd9e417e71 100644 --- a/src/firecracker/src/api_server/mod.rs +++ b/src/firecracker/src/api_server/mod.rs @@ -14,13 +14,13 @@ use std::sync::mpsc; pub use micro_http::{Body, HttpServer, Request, Response, ServerError, StatusCode, Version}; use parsed_request::{ParsedRequest, RequestAction}; -use seccompiler::BpfProgramRef; use serde_json::json; -use utils::time::{get_time_us, ClockType}; +use utils::time::{ClockType, get_time_us}; use vmm::logger::{ - debug, error, info, update_metric_with_elapsed_time, warn, ProcessTimeReporter, METRICS, + METRICS, ProcessTimeReporter, debug, error, info, update_metric_with_elapsed_time, warn, }; use vmm::rpc_interface::{ApiRequest, ApiResponse, VmmAction}; +use vmm::seccomp::BpfProgramRef; use vmm::vmm_config::snapshot::SnapshotType; use vmm_sys_util::eventfd::EventFd; @@ -78,7 +78,7 @@ impl ApiServer { // Load seccomp filters on the API thread. // Execution panics if filters cannot be loaded, use --no-seccomp if skipping filters // altogether is the desired behaviour. - if let Err(err) = seccompiler::apply_filter(seccomp_filter) { + if let Err(err) = vmm::seccomp::apply_filter(seccomp_filter) { panic!( "Failed to set the requested seccomp filters on the API thread: {}", err @@ -208,7 +208,7 @@ mod tests { use vmm::builder::StartMicrovmError; use vmm::logger::StoreMetric; use vmm::rpc_interface::{VmmActionError, VmmData}; - use vmm::seccomp_filters::get_empty_filters; + use vmm::seccomp::get_empty_filters; use vmm::vmm_config::instance_info::InstanceInfo; use vmm::vmm_config::snapshot::CreateSnapshotParams; use vmm_sys_util::tempfile::TempFile; diff --git a/src/firecracker/src/api_server/parsed_request.rs b/src/firecracker/src/api_server/parsed_request.rs index 125463d1d05..10d5c3d97ea 100644 --- a/src/firecracker/src/api_server/parsed_request.rs +++ b/src/firecracker/src/api_server/parsed_request.rs @@ -6,9 +6,10 @@ use std::fmt::Debug; use micro_http::{Body, Method, Request, Response, StatusCode, Version}; use serde::ser::Serialize; use serde_json::Value; -use vmm::logger::{error, info, log_enabled, Level}; +use vmm::logger::{Level, error, info, log_enabled}; use vmm::rpc_interface::{VmmAction, VmmActionError, VmmData}; +use super::ApiServer; use super::request::actions::parse_put_actions; use super::request::balloon::{parse_get_balloon, parse_patch_balloon, parse_put_balloon}; use super::request::boot_source::parse_put_boot_source; @@ -26,7 +27,6 @@ use super::request::net::{parse_patch_net, parse_put_net}; use super::request::snapshot::{parse_patch_vm_state, parse_put_snapshot}; use super::request::version::parse_get_version; use super::request::vsock::parse_put_vsock; -use super::ApiServer; #[derive(Debug)] pub(crate) enum RequestAction { @@ -163,8 +163,8 @@ impl ParsedRequest { info!("The request was executed successfully. Status code: 204 No Content."); Response::new(Version::Http11, StatusCode::NoContent) } - VmmData::MachineConfiguration(vm_config) => { - Self::success_response_with_data(vm_config) + VmmData::MachineConfiguration(machine_config) => { + Self::success_response_with_data(machine_config) } VmmData::MmdsValue(value) => Self::success_response_with_mmds_value(value), VmmData::BalloonConfig(balloon_config) => { @@ -276,7 +276,7 @@ pub(crate) enum RequestError { #[error("API Resource IDs can only contain alphanumeric characters and underscores.")] InvalidID, // The HTTP method & request path combination is not valid. - #[error("Invalid request method and/or path: {} {0}.", .1.to_str())] + #[error("Invalid request method and/or path: {} {}.", .1.to_str(), .0)] InvalidPathMethod(String, Method), // An error occurred when deserializing the json body of a request. #[error("An error occurred when deserializing the json body of a request: {0}.")] @@ -336,7 +336,7 @@ pub mod tests { } match (&self.action, &other.action) { - (RequestAction::Sync(ref sync_req), RequestAction::Sync(ref other_sync_req)) => { + (RequestAction::Sync(sync_req), RequestAction::Sync(other_sync_req)) => { sync_req == other_sync_req } } diff --git a/src/firecracker/src/api_server/request/actions.rs b/src/firecracker/src/api_server/request/actions.rs index 7d7974571a9..a3b3f3f3a88 100644 --- a/src/firecracker/src/api_server/request/actions.rs +++ b/src/firecracker/src/api_server/request/actions.rs @@ -30,9 +30,8 @@ struct ActionBody { pub(crate) fn parse_put_actions(body: &Body) -> Result { METRICS.put_api_requests.actions_count.inc(); - let action_body = serde_json::from_slice::(body.raw()).map_err(|err| { + let action_body = serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.put_api_requests.actions_fails.inc(); - err })?; match action_body.action_type { diff --git a/src/firecracker/src/api_server/request/boot_source.rs b/src/firecracker/src/api_server/request/boot_source.rs index 16f3c1b5499..10e16148461 100644 --- a/src/firecracker/src/api_server/request/boot_source.rs +++ b/src/firecracker/src/api_server/request/boot_source.rs @@ -11,9 +11,8 @@ use super::Body; pub(crate) fn parse_put_boot_source(body: &Body) -> Result { METRICS.put_api_requests.boot_source_count.inc(); Ok(ParsedRequest::new_sync(VmmAction::ConfigureBootSource( - serde_json::from_slice::(body.raw()).map_err(|err| { + serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.put_api_requests.boot_source_fails.inc(); - err })?, ))) } diff --git a/src/firecracker/src/api_server/request/cpu_configuration.rs b/src/firecracker/src/api_server/request/cpu_configuration.rs index 649182bdf7c..454df80be4b 100644 --- a/src/firecracker/src/api_server/request/cpu_configuration.rs +++ b/src/firecracker/src/api_server/request/cpu_configuration.rs @@ -23,7 +23,7 @@ pub(crate) fn parse_put_cpu_config(body: &Body) -> Result(body.raw()).map_err(|err| { + let device_cfg = serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.put_api_requests.drive_fails.inc(); - err })?; if id != device_cfg.drive_id { @@ -51,9 +50,8 @@ pub(crate) fn parse_patch_drive( }; let block_device_update_cfg: BlockDeviceUpdateConfig = - serde_json::from_slice::(body.raw()).map_err(|err| { + serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.patch_api_requests.drive_fails.inc(); - err })?; if id != block_device_update_cfg.drive_id { diff --git a/src/firecracker/src/api_server/request/logger.rs b/src/firecracker/src/api_server/request/logger.rs index 6355bf48beb..cda125ac71c 100644 --- a/src/firecracker/src/api_server/request/logger.rs +++ b/src/firecracker/src/api_server/request/logger.rs @@ -10,9 +10,8 @@ use super::Body; pub(crate) fn parse_put_logger(body: &Body) -> Result { METRICS.put_api_requests.logger_count.inc(); let res = serde_json::from_slice::(body.raw()); - let config = res.map_err(|err| { + let config = res.inspect_err(|_| { METRICS.put_api_requests.logger_fails.inc(); - err })?; Ok(ParsedRequest::new_sync(VmmAction::ConfigureLogger(config))) } diff --git a/src/firecracker/src/api_server/request/machine_configuration.rs b/src/firecracker/src/api_server/request/machine_configuration.rs index 344d9095b77..2e8addffb74 100644 --- a/src/firecracker/src/api_server/request/machine_configuration.rs +++ b/src/firecracker/src/api_server/request/machine_configuration.rs @@ -5,7 +5,7 @@ use vmm::logger::{IncMetric, METRICS}; use vmm::rpc_interface::VmmAction; use vmm::vmm_config::machine_config::{MachineConfig, MachineConfigUpdate}; -use super::super::parsed_request::{method_to_error, ParsedRequest, RequestError}; +use super::super::parsed_request::{ParsedRequest, RequestError, method_to_error}; use super::{Body, Method}; pub(crate) fn parse_get_machine_config() -> Result { @@ -15,9 +15,8 @@ pub(crate) fn parse_get_machine_config() -> Result pub(crate) fn parse_put_machine_config(body: &Body) -> Result { METRICS.put_api_requests.machine_cfg_count.inc(); - let config = serde_json::from_slice::(body.raw()).map_err(|err| { + let config = serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.put_api_requests.machine_cfg_fails.inc(); - err })?; // Check for the presence of deprecated `cpu_template` field. @@ -32,7 +31,8 @@ pub(crate) fn parse_put_machine_config(body: &Body) -> Result Result Result { METRICS.patch_api_requests.machine_cfg_count.inc(); let config_update = - serde_json::from_slice::(body.raw()).map_err(|err| { + serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.patch_api_requests.machine_cfg_fails.inc(); - err })?; if config_update.is_empty() { @@ -62,7 +61,8 @@ pub(crate) fn parse_patch_machine_config(body: &Body) -> Result Result { METRICS.put_api_requests.metrics_count.inc(); Ok(ParsedRequest::new_sync(VmmAction::ConfigureMetrics( - serde_json::from_slice::(body.raw()).map_err(|err| { + serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.put_api_requests.metrics_fails.inc(); - err })?, ))) } diff --git a/src/firecracker/src/api_server/request/mmds.rs b/src/firecracker/src/api_server/request/mmds.rs index ced0a671e9b..2bc96512d3c 100644 --- a/src/firecracker/src/api_server/request/mmds.rs +++ b/src/firecracker/src/api_server/request/mmds.rs @@ -16,9 +16,8 @@ pub(crate) fn parse_get_mmds() -> Result { } fn parse_put_mmds_config(body: &Body) -> Result { - let config: MmdsConfig = serde_json::from_slice(body.raw()).map_err(|err| { + let config: MmdsConfig = serde_json::from_slice(body.raw()).inspect_err(|_| { METRICS.put_api_requests.mmds_fails.inc(); - err })?; // Construct the `ParsedRequest` object. let version = config.version; @@ -42,9 +41,8 @@ pub(crate) fn parse_put_mmds( METRICS.put_api_requests.mmds_count.inc(); match path_second_token { None => Ok(ParsedRequest::new_sync(VmmAction::PutMMDS( - serde_json::from_slice(body.raw()).map_err(|err| { + serde_json::from_slice(body.raw()).inspect_err(|_| { METRICS.put_api_requests.mmds_fails.inc(); - err })?, ))), Some("config") => parse_put_mmds_config(body), @@ -61,9 +59,8 @@ pub(crate) fn parse_put_mmds( pub(crate) fn parse_patch_mmds(body: &Body) -> Result { METRICS.patch_api_requests.mmds_count.inc(); Ok(ParsedRequest::new_sync(VmmAction::PatchMMDS( - serde_json::from_slice(body.raw()).map_err(|err| { + serde_json::from_slice(body.raw()).inspect_err(|_| { METRICS.patch_api_requests.mmds_fails.inc(); - err })?, ))) } diff --git a/src/firecracker/src/api_server/request/net.rs b/src/firecracker/src/api_server/request/net.rs index 0ab5377b0a1..a0125add274 100644 --- a/src/firecracker/src/api_server/request/net.rs +++ b/src/firecracker/src/api_server/request/net.rs @@ -5,7 +5,7 @@ use vmm::logger::{IncMetric, METRICS}; use vmm::rpc_interface::VmmAction; use vmm::vmm_config::net::{NetworkInterfaceConfig, NetworkInterfaceUpdateConfig}; -use super::super::parsed_request::{checked_id, ParsedRequest, RequestError}; +use super::super::parsed_request::{ParsedRequest, RequestError, checked_id}; use super::{Body, StatusCode}; pub(crate) fn parse_put_net( @@ -20,9 +20,8 @@ pub(crate) fn parse_put_net( return Err(RequestError::EmptyID); }; - let netif = serde_json::from_slice::(body.raw()).map_err(|err| { + let netif = serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.put_api_requests.network_fails.inc(); - err })?; if id != netif.iface_id.as_str() { METRICS.put_api_requests.network_fails.inc(); @@ -53,9 +52,8 @@ pub(crate) fn parse_patch_net( }; let netif = - serde_json::from_slice::(body.raw()).map_err(|err| { + serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.patch_api_requests.network_fails.inc(); - err })?; if id != netif.iface_id { METRICS.patch_api_requests.network_count.inc(); diff --git a/src/firecracker/src/api_server/request/snapshot.rs b/src/firecracker/src/api_server/request/snapshot.rs index 8878c224b5c..4a96292d11d 100644 --- a/src/firecracker/src/api_server/request/snapshot.rs +++ b/src/firecracker/src/api_server/request/snapshot.rs @@ -66,13 +66,13 @@ fn parse_put_snapshot_load(body: &Body) -> Result { (Some(_), Some(_)) => { return Err(RequestError::SerdeJson(serde_json::Error::custom( TOO_MANY_FIELDS, - ))) + ))); } // Ensure that one of `mem_file_path` or `mem_backend` fields is always specified. (None, None) => { return Err(RequestError::SerdeJson(serde_json::Error::custom( MISSING_FIELD, - ))) + ))); } _ => {} } @@ -105,6 +105,7 @@ fn parse_put_snapshot_load(body: &Body) -> Result { mem_backend, enable_diff_snapshots: snapshot_config.enable_diff_snapshots, resume_vm: snapshot_config.resume_vm, + network_overrides: snapshot_config.network_overrides, }; // Construct the `ParsedRequest` object. @@ -120,7 +121,7 @@ fn parse_put_snapshot_load(body: &Body) -> Result { #[cfg(test)] mod tests { - use vmm::vmm_config::snapshot::{MemBackendConfig, MemBackendType}; + use vmm::vmm_config::snapshot::{MemBackendConfig, MemBackendType, NetworkOverride}; use super::*; use crate::api_server::parsed_request::tests::{depr_action_from_req, vmm_action_from_request}; @@ -181,12 +182,15 @@ mod tests { }, enable_diff_snapshots: false, resume_vm: false, + network_overrides: vec![], }; let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap(); - assert!(parsed_request - .parsing_info() - .take_deprecation_message() - .is_none()); + assert!( + parsed_request + .parsing_info() + .take_deprecation_message() + .is_none() + ); assert_eq!( vmm_action_from_request(parsed_request), VmmAction::LoadSnapshot(expected_config) @@ -208,12 +212,15 @@ mod tests { }, enable_diff_snapshots: true, resume_vm: false, + network_overrides: vec![], }; let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap(); - assert!(parsed_request - .parsing_info() - .take_deprecation_message() - .is_none()); + assert!( + parsed_request + .parsing_info() + .take_deprecation_message() + .is_none() + ); assert_eq!( vmm_action_from_request(parsed_request), VmmAction::LoadSnapshot(expected_config) @@ -235,12 +242,54 @@ mod tests { }, enable_diff_snapshots: false, resume_vm: true, + network_overrides: vec![], + }; + let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap(); + assert!( + parsed_request + .parsing_info() + .take_deprecation_message() + .is_none() + ); + assert_eq!( + vmm_action_from_request(parsed_request), + VmmAction::LoadSnapshot(expected_config) + ); + + let body = r#"{ + "snapshot_path": "foo", + "mem_backend": { + "backend_path": "bar", + "backend_type": "Uffd" + }, + "resume_vm": true, + "network_overrides": [ + { + "iface_id": "eth0", + "host_dev_name": "vmtap2" + } + ] + }"#; + let expected_config = LoadSnapshotParams { + snapshot_path: PathBuf::from("foo"), + mem_backend: MemBackendConfig { + backend_path: PathBuf::from("bar"), + backend_type: MemBackendType::Uffd, + }, + enable_diff_snapshots: false, + resume_vm: true, + network_overrides: vec![NetworkOverride { + iface_id: String::from("eth0"), + host_dev_name: String::from("vmtap2"), + }], }; let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap(); - assert!(parsed_request - .parsing_info() - .take_deprecation_message() - .is_none()); + assert!( + parsed_request + .parsing_info() + .take_deprecation_message() + .is_none() + ); assert_eq!( vmm_action_from_request(parsed_request), VmmAction::LoadSnapshot(expected_config) @@ -259,6 +308,7 @@ mod tests { }, enable_diff_snapshots: false, resume_vm: true, + network_overrides: vec![], }; let parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap(); assert_eq!( @@ -348,16 +398,20 @@ mod tests { let body = r#"{ "state": "Paused" }"#; - assert!(parse_patch_vm_state(&Body::new(body)) - .unwrap() - .eq(&ParsedRequest::new_sync(VmmAction::Pause))); + assert!( + parse_patch_vm_state(&Body::new(body)) + .unwrap() + .eq(&ParsedRequest::new_sync(VmmAction::Pause)) + ); let body = r#"{ "state": "Resumed" }"#; - assert!(parse_patch_vm_state(&Body::new(body)) - .unwrap() - .eq(&ParsedRequest::new_sync(VmmAction::Resume))); + assert!( + parse_patch_vm_state(&Body::new(body)) + .unwrap() + .eq(&ParsedRequest::new_sync(VmmAction::Resume)) + ); let invalid_body = r#"{ "invalid": "Paused" diff --git a/src/firecracker/src/api_server/request/vsock.rs b/src/firecracker/src/api_server/request/vsock.rs index 67bf7b0a985..acf129d456c 100644 --- a/src/firecracker/src/api_server/request/vsock.rs +++ b/src/firecracker/src/api_server/request/vsock.rs @@ -10,9 +10,8 @@ use super::Body; pub(crate) fn parse_put_vsock(body: &Body) -> Result { METRICS.put_api_requests.vsock_count.inc(); - let vsock_cfg = serde_json::from_slice::(body.raw()).map_err(|err| { + let vsock_cfg = serde_json::from_slice::(body.raw()).inspect_err(|_| { METRICS.put_api_requests.vsock_fails.inc(); - err })?; // Check for the presence of deprecated `vsock_id` field. diff --git a/src/firecracker/src/api_server_adapter.rs b/src/firecracker/src/api_server_adapter.rs index ffc4732025d..b6178a8cbf7 100644 --- a/src/firecracker/src/api_server_adapter.rs +++ b/src/firecracker/src/api_server_adapter.rs @@ -3,18 +3,18 @@ use std::os::unix::io::AsRawFd; use std::path::PathBuf; -use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; +use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel}; use std::sync::{Arc, Mutex}; use std::thread; use event_manager::{EventOps, Events, MutEventSubscriber, SubscriberOps}; -use seccompiler::BpfThreadMap; -use vmm::logger::{error, warn, ProcessTimeReporter}; +use vmm::logger::{ProcessTimeReporter, error, warn}; use vmm::resources::VmResources; use vmm::rpc_interface::{ ApiRequest, ApiResponse, BuildMicrovmFromRequestsError, PrebootApiController, RuntimeApiController, VmmAction, }; +use vmm::seccomp::BpfThreadMap; use vmm::vmm_config::instance_info::InstanceInfo; use vmm::{EventManager, FcExitCode, Vmm}; use vmm_sys_util::epoll::EventSet; diff --git a/src/firecracker/src/gen/mod.rs b/src/firecracker/src/generated/mod.rs similarity index 100% rename from src/firecracker/src/gen/mod.rs rename to src/firecracker/src/generated/mod.rs diff --git a/src/firecracker/src/gen/prctl.rs b/src/firecracker/src/generated/prctl.rs similarity index 81% rename from src/firecracker/src/gen/prctl.rs rename to src/firecracker/src/generated/prctl.rs index e46a41ce5e9..c4c15df92d3 100644 --- a/src/firecracker/src/gen/prctl.rs +++ b/src/firecracker/src/generated/prctl.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,7 +11,8 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] pub const PR_SET_PDEATHSIG: u32 = 1; @@ -152,3 +153,29 @@ pub const PR_SCHED_CORE_CREATE: u32 = 1; pub const PR_SCHED_CORE_SHARE_TO: u32 = 2; pub const PR_SCHED_CORE_SHARE_FROM: u32 = 3; pub const PR_SCHED_CORE_MAX: u32 = 4; +pub const PR_SCHED_CORE_SCOPE_THREAD: u32 = 0; +pub const PR_SCHED_CORE_SCOPE_THREAD_GROUP: u32 = 1; +pub const PR_SCHED_CORE_SCOPE_PROCESS_GROUP: u32 = 2; +pub const PR_SME_SET_VL: u32 = 63; +pub const PR_SME_SET_VL_ONEXEC: u32 = 262144; +pub const PR_SME_GET_VL: u32 = 64; +pub const PR_SME_VL_LEN_MASK: u32 = 65535; +pub const PR_SME_VL_INHERIT: u32 = 131072; +pub const PR_SET_MDWE: u32 = 65; +pub const PR_MDWE_REFUSE_EXEC_GAIN: u32 = 1; +pub const PR_MDWE_NO_INHERIT: u32 = 2; +pub const PR_GET_MDWE: u32 = 66; +pub const PR_SET_VMA: u32 = 1398164801; +pub const PR_SET_VMA_ANON_NAME: u32 = 0; +pub const PR_GET_AUXV: u32 = 1096112214; +pub const PR_SET_MEMORY_MERGE: u32 = 67; +pub const PR_GET_MEMORY_MERGE: u32 = 68; +pub const PR_RISCV_V_SET_CONTROL: u32 = 69; +pub const PR_RISCV_V_GET_CONTROL: u32 = 70; +pub const PR_RISCV_V_VSTATE_CTRL_DEFAULT: u32 = 0; +pub const PR_RISCV_V_VSTATE_CTRL_OFF: u32 = 1; +pub const PR_RISCV_V_VSTATE_CTRL_ON: u32 = 2; +pub const PR_RISCV_V_VSTATE_CTRL_INHERIT: u32 = 16; +pub const PR_RISCV_V_VSTATE_CTRL_CUR_MASK: u32 = 3; +pub const PR_RISCV_V_VSTATE_CTRL_NEXT_MASK: u32 = 12; +pub const PR_RISCV_V_VSTATE_CTRL_MASK: u32 = 31; diff --git a/src/firecracker/src/main.rs b/src/firecracker/src/main.rs index 8fb5392afcf..6b01f776729 100644 --- a/src/firecracker/src/main.rs +++ b/src/firecracker/src/main.rs @@ -3,7 +3,7 @@ mod api_server; mod api_server_adapter; -mod gen; +mod generated; mod metrics; mod seccomp; @@ -17,19 +17,20 @@ use std::{io, panic}; use api_server_adapter::ApiServerError; use event_manager::SubscriberOps; use seccomp::FilterError; -use seccompiler::BpfThreadMap; use utils::arg_parser::{ArgParser, Argument}; use utils::validators::validate_instance_id; +use vmm::arch::host_page_size; use vmm::builder::StartMicrovmError; use vmm::logger::{ - debug, error, info, LoggerConfig, ProcessTimeReporter, StoreMetric, LOGGER, METRICS, + LOGGER, LoggerConfig, METRICS, ProcessTimeReporter, StoreMetric, debug, error, info, }; use vmm::persist::SNAPSHOT_VERSION; use vmm::resources::VmResources; +use vmm::seccomp::BpfThreadMap; use vmm::signal_handler::register_signal_handlers; use vmm::snapshot::{Snapshot, SnapshotError}; use vmm::vmm_config::instance_info::{InstanceInfo, VmState}; -use vmm::vmm_config::metrics::{init_metrics, MetricsConfig, MetricsConfigError}; +use vmm::vmm_config::metrics::{MetricsConfig, MetricsConfigError, init_metrics}; use vmm::{EventManager, FcExitCode, HTTP_MAX_PAYLOAD_SIZE}; use vmm_sys_util::terminal::Terminal; @@ -108,6 +109,10 @@ fn main_exec() -> Result<(), MainError> { // Initialize the logger. LOGGER.init().map_err(MainError::SetLogger)?; + // First call to this function updates the value to current + // host page size. + _ = host_page_size(); + // We need this so that we can reset terminal to canonical mode if panic occurs. let stdin = io::stdin(); @@ -496,9 +501,9 @@ pub fn enable_ssbd_mitigation() { // to leave the latter 2 as zero. let ret = unsafe { libc::prctl( - gen::prctl::PR_SET_SPECULATION_CTRL, - gen::prctl::PR_SPEC_STORE_BYPASS, - gen::prctl::PR_SPEC_FORCE_DISABLE, + generated::prctl::PR_SET_SPECULATION_CTRL, + generated::prctl::PR_SPEC_STORE_BYPASS, + generated::prctl::PR_SPEC_FORCE_DISABLE, 0, 0, ) diff --git a/src/firecracker/src/metrics.rs b/src/firecracker/src/metrics.rs index 9eaffd47811..80f21257107 100644 --- a/src/firecracker/src/metrics.rs +++ b/src/firecracker/src/metrics.rs @@ -6,7 +6,7 @@ use std::time::Duration; use event_manager::{EventOps, Events, MutEventSubscriber}; use timerfd::{ClockId, SetTimeFlags, TimerFd, TimerState}; -use vmm::logger::{error, warn, IncMetric, METRICS}; +use vmm::logger::{IncMetric, METRICS, error, warn}; use vmm_sys_util::epoll::EventSet; /// Metrics reporting period. diff --git a/src/firecracker/src/seccomp.rs b/src/firecracker/src/seccomp.rs index 5794d6498a8..421220a7b5f 100644 --- a/src/firecracker/src/seccomp.rs +++ b/src/firecracker/src/seccomp.rs @@ -5,17 +5,10 @@ use std::fs::File; use std::io::{BufReader, Read}; use std::path::Path; -use seccompiler::{deserialize_binary, BpfThreadMap, DeserializationError}; -use vmm::seccomp_filters::get_empty_filters; +use vmm::seccomp::{BpfThreadMap, DeserializationError, deserialize_binary, get_empty_filters}; const THREAD_CATEGORIES: [&str; 3] = ["vmm", "api", "vcpu"]; -// This byte limit is passed to `bincode` to guard against a potential memory -// allocation DOS caused by binary filters that are too large. -// This limit can be safely determined since the maximum length of a BPF -// filter is 4096 instructions and Firecracker has a finite number of threads. -const DESERIALIZATION_BYTES_LIMIT: Option = Some(100_000); - /// Error retrieving seccomp filters. #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum FilterError { @@ -73,15 +66,13 @@ pub fn get_filters(config: SeccompConfig) -> Result { fn get_default_filters() -> Result { // Retrieve, at compile-time, the serialized binary filter generated with seccompiler. let bytes: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/seccomp_filter.bpf")); - let map = deserialize_binary(bytes, DESERIALIZATION_BYTES_LIMIT) - .map_err(FilterError::Deserialization)?; + let map = deserialize_binary(bytes).map_err(FilterError::Deserialization)?; filter_thread_categories(map) } /// Retrieve custom seccomp filters. fn get_custom_filters(reader: R) -> Result { - let map = deserialize_binary(BufReader::new(reader), DESERIALIZATION_BYTES_LIMIT) - .map_err(FilterError::Deserialization)?; + let map = deserialize_binary(BufReader::new(reader)).map_err(FilterError::Deserialization)?; filter_thread_categories(map) } @@ -118,7 +109,7 @@ fn filter_thread_categories(map: BpfThreadMap) -> Result"] -edition = "2021" +edition = "2024" description = "Process for starting Firecracker in production scenarios; applies a cgroup/namespace isolation barrier and then drops privileges." homepage = "https://firecracker-microvm.github.io/" license = "Apache-2.0" @@ -12,11 +12,10 @@ name = "jailer" bench = false [dependencies] -libc = "0.2.161" +libc = "0.2.171" log-instrument = { path = "../log-instrument", optional = true } -nix = { version = "0.29.0", default-features = false, features = ["dir"] } regex = { version = "1.11.1", default-features = false, features = ["std"] } -thiserror = "1.0.67" +thiserror = "2.0.12" vmm-sys-util = "0.12.1" utils = { path = "../utils" } diff --git a/src/jailer/src/cgroup.rs b/src/jailer/src/cgroup.rs index e49b1acb317..94098d3698f 100644 --- a/src/jailer/src/cgroup.rs +++ b/src/jailer/src/cgroup.rs @@ -11,7 +11,7 @@ use std::process; use regex::Regex; -use crate::{readln_special, writeln_special, JailerError}; +use crate::{JailerError, readln_special, writeln_special}; // Holds information on a cgroup mount point discovered on the system #[derive(Debug)] @@ -234,8 +234,8 @@ pub enum CgroupConfiguration { impl CgroupConfiguration { pub fn setup(&self) -> Result<(), JailerError> { match self { - Self::V1(ref conf) => setup_cgroup_conf(conf), - Self::V2(ref conf) => setup_cgroup_conf(conf), + Self::V1(conf) => setup_cgroup_conf(conf), + Self::V2(conf) => setup_cgroup_conf(conf), } } } @@ -817,9 +817,11 @@ mod tests { ); // check that the controller was enabled in all parent dirs - assert!(read_first_line(cg_root.join("cgroup.subtree_control")) - .unwrap() - .contains("cpuset")); + assert!( + read_first_line(cg_root.join("cgroup.subtree_control")) + .unwrap() + .contains("cpuset") + ); assert!( read_first_line(cg_root.join("fc_test_cgv2/cgroup.subtree_control")) .unwrap() diff --git a/src/jailer/src/chroot.rs b/src/jailer/src/chroot.rs index 44386729196..56335c03a74 100644 --- a/src/jailer/src/chroot.rs +++ b/src/jailer/src/chroot.rs @@ -8,11 +8,11 @@ use std::ptr::null; use vmm_sys_util::syscall::SyscallReturnCode; -use super::{to_cstring, JailerError}; +use super::{JailerError, to_cstring}; -const OLD_ROOT_DIR_NAME_NUL_TERMINATED: &[u8] = b"old_root\0"; -const ROOT_DIR_NUL_TERMINATED: &[u8] = b"/\0"; -const CURRENT_DIR_NUL_TERMINATED: &[u8] = b".\0"; +const OLD_ROOT_DIR: &CStr = c"old_root"; +const ROOT_DIR: &CStr = c"/"; +const CURRENT_DIR: &CStr = c"."; // This uses switching to a new mount namespace + pivot_root(), together with the regular chroot, // to provide a hardened jail (at least compared to only relying on chroot). @@ -24,16 +24,13 @@ pub fn chroot(path: &Path) -> Result<(), JailerError> { .into_empty_result() .map_err(JailerError::UnshareNewNs)?; - let root_dir = CStr::from_bytes_with_nul(ROOT_DIR_NUL_TERMINATED) - .map_err(JailerError::FromBytesWithNul)?; - // Recursively change the propagation type of all the mounts in this namespace to SLAVE, so // we can call pivot_root. // SAFETY: Safe because we provide valid parameters. SyscallReturnCode(unsafe { libc::mount( null(), - root_dir.as_ptr(), + ROOT_DIR.as_ptr(), null(), libc::MS_SLAVE | libc::MS_REC, null(), @@ -64,25 +61,21 @@ pub fn chroot(path: &Path) -> Result<(), JailerError> { // Change current dir to the chroot dir, so we only need to handle relative paths from now on. env::set_current_dir(path).map_err(JailerError::SetCurrentDir)?; - // We use the CStr conversion to make sure the contents of the byte slice would be a - // valid C string (and for the as_ptr() method). - let old_root_dir = CStr::from_bytes_with_nul(OLD_ROOT_DIR_NAME_NUL_TERMINATED) - .map_err(JailerError::FromBytesWithNul)?; - // Create the old_root folder we're going to use for pivot_root, using a relative path. // SAFETY: The call is safe because we provide valid arguments. - SyscallReturnCode(unsafe { libc::mkdir(old_root_dir.as_ptr(), libc::S_IRUSR | libc::S_IWUSR) }) + SyscallReturnCode(unsafe { libc::mkdir(OLD_ROOT_DIR.as_ptr(), libc::S_IRUSR | libc::S_IWUSR) }) .into_empty_result() .map_err(JailerError::MkdirOldRoot)?; - let cwd = CStr::from_bytes_with_nul(CURRENT_DIR_NUL_TERMINATED) - .map_err(JailerError::FromBytesWithNul)?; - // We are now ready to call pivot_root. We have to use sys_call because there is no libc // wrapper for pivot_root. // SAFETY: Safe because we provide valid parameters. SyscallReturnCode(unsafe { - libc::syscall(libc::SYS_pivot_root, cwd.as_ptr(), old_root_dir.as_ptr()) + libc::syscall( + libc::SYS_pivot_root, + CURRENT_DIR.as_ptr(), + OLD_ROOT_DIR.as_ptr(), + ) }) .into_empty_result() .map_err(JailerError::PivotRoot)?; @@ -90,19 +83,19 @@ pub fn chroot(path: &Path) -> Result<(), JailerError> { // pivot_root doesn't guarantee that we will be in "/" at this point, so switch to "/" // explicitly. // SAFETY: Safe because we provide valid parameters. - SyscallReturnCode(unsafe { libc::chdir(root_dir.as_ptr()) }) + SyscallReturnCode(unsafe { libc::chdir(ROOT_DIR.as_ptr()) }) .into_empty_result() .map_err(JailerError::ChdirNewRoot)?; // Umount the old_root, thus isolating the process from everything outside the jail root folder. // SAFETY: Safe because we provide valid parameters. - SyscallReturnCode(unsafe { libc::umount2(old_root_dir.as_ptr(), libc::MNT_DETACH) }) + SyscallReturnCode(unsafe { libc::umount2(OLD_ROOT_DIR.as_ptr(), libc::MNT_DETACH) }) .into_empty_result() .map_err(JailerError::UmountOldRoot)?; // Remove the no longer necessary old_root directory. // SAFETY: Safe because we provide valid parameters. - SyscallReturnCode(unsafe { libc::rmdir(old_root_dir.as_ptr()) }) + SyscallReturnCode(unsafe { libc::rmdir(OLD_ROOT_DIR.as_ptr()) }) .into_empty_result() .map_err(JailerError::RmOldRootDir) } diff --git a/src/jailer/src/env.rs b/src/jailer/src/env.rs index a0c37eac540..e337ea95f90 100644 --- a/src/jailer/src/env.rs +++ b/src/jailer/src/env.rs @@ -1,25 +1,25 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use std::ffi::{CString, OsString}; -use std::fs::{self, canonicalize, read_to_string, File, OpenOptions, Permissions}; +use std::ffi::{CStr, CString, OsString}; +use std::fs::{self, File, OpenOptions, Permissions, canonicalize, read_to_string}; +use std::io; use std::io::Write; use std::os::unix::fs::PermissionsExt; use std::os::unix::io::AsRawFd; use std::os::unix::process::CommandExt; use std::path::{Component, Path, PathBuf}; -use std::process::{exit, id, Command, Stdio}; -use std::{fmt, io}; +use std::process::{Command, Stdio, exit, id}; use utils::arg_parser::UtilsArgParserError::MissingValue; -use utils::time::{get_time_us, ClockType}; +use utils::time::{ClockType, get_time_us}; use utils::{arg_parser, validators}; use vmm_sys_util::syscall::SyscallReturnCode; +use crate::JailerError; use crate::cgroup::{CgroupConfiguration, CgroupConfigurationBuilder}; use crate::chroot::chroot; -use crate::resource_limits::{ResourceLimits, FSIZE_ARG, NO_FILE_ARG}; -use crate::JailerError; +use crate::resource_limits::{FSIZE_ARG, NO_FILE_ARG, ResourceLimits}; pub const PROC_MOUNTS: &str = "/proc/mounts"; @@ -30,19 +30,19 @@ const STDERR_FILENO: libc::c_int = 2; // Kernel-based virtual machine (hardware virtualization extensions) // minor/major numbers are taken from // https://www.kernel.org/doc/html/latest/admin-guide/devices.html -const DEV_KVM_WITH_NUL: &str = "/dev/kvm"; +const DEV_KVM: &CStr = c"/dev/kvm"; const DEV_KVM_MAJOR: u32 = 10; const DEV_KVM_MINOR: u32 = 232; // TUN/TAP device minor/major numbers are taken from // www.kernel.org/doc/Documentation/networking/tuntap.txt -const DEV_NET_TUN_WITH_NUL: &str = "/dev/net/tun"; +const DEV_NET_TUN: &CStr = c"/dev/net/tun"; const DEV_NET_TUN_MAJOR: u32 = 10; const DEV_NET_TUN_MINOR: u32 = 200; // Random number generator device minor/major numbers are taken from // https://www.kernel.org/doc/Documentation/admin-guide/devices.txt -const DEV_URANDOM_WITH_NUL: &str = "/dev/urandom"; +const DEV_URANDOM: &CStr = c"/dev/urandom"; const DEV_URANDOM_MAJOR: u32 = 1; const DEV_URANDOM_MINOR: u32 = 9; @@ -54,7 +54,7 @@ const DEV_URANDOM_MINOR: u32 = 9; // so we will have to find it at initialization time parsing /proc/misc. // What we do know is the major number for misc devices: // https://elixir.bootlin.com/linux/v6.1.51/source/Documentation/admin-guide/devices.txt -const DEV_UFFD_PATH: &str = "/dev/userfaultfd"; +const DEV_UFFD_PATH: &CStr = c"/dev/userfaultfd"; const DEV_UFFD_MAJOR: u32 = 10; // Relevant folders inside the jail that we create or/and for which we change ownership. @@ -114,6 +114,7 @@ enum UserfaultfdParseError { NotFound, } +#[derive(Debug)] pub struct Env { id: String, chroot_dir: PathBuf, @@ -132,26 +133,6 @@ pub struct Env { uffd_dev_minor: Option, } -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Env") - .field("id", &self.id) - .field("chroot_dir", &self.chroot_dir) - .field("exec_file_path", &self.exec_file_path) - .field("uid", &self.uid) - .field("gid", &self.gid) - .field("netns", &self.netns) - .field("daemonize", &self.daemonize) - .field("new_pid_ns", &self.new_pid_ns) - .field("start_time_us", &self.start_time_us) - .field("jailer_cpu_time_us", &self.jailer_cpu_time_us) - .field("extra_args", &self.extra_args) - .field("cgroups", &self.cgroup_conf) - .field("resource_limits", &self.resource_limits) - .finish() - } -} - impl Env { pub fn new( arguments: &arg_parser::Arguments, @@ -349,12 +330,52 @@ impl Env { } fn exec_into_new_pid_ns(&mut self, chroot_exec_file: PathBuf) -> Result<(), JailerError> { + // https://man7.org/linux/man-pages/man7/pid_namespaces.7.html + // > a process in an ancestor namespace can send signals to the "init" process of a child + // > PID namespace only if the "init" process has established a handler for that signal. + // + // Firecracker (i.e. the "init" process of the new PID namespace) sets up handlers for some + // signals including SIGHUP and jailer exits soon after spawning firecracker into a new PID + // namespace. If the jailer process is a session leader and its exit happens after + // firecracker configures the signal handlers, SIGHUP will be sent to firecracker and be + // caught by the handler unexpectedly. + // + // In order to avoid the above issue, if jailer is a session leader, creates a new session + // and makes the child process (i.e. firecracker) become the leader of the new session to + // not get SIGHUP on the exit of jailer. + + // Check whether jailer is a session leader or not before clone(). + // Note that, if `--daemonize` is passed, jailer is always not a session leader. This is + // because we use the double fork method, making itself not a session leader. + let is_session_leader = match self.daemonize { + true => false, + false => { + // SAFETY: Safe because it doesn't take any input parameters. + let sid = SyscallReturnCode(unsafe { libc::getsid(0) }) + .into_result() + .map_err(JailerError::GetSid)?; + // SAFETY: Safe because it doesn't take any input parameters. + let ppid = SyscallReturnCode(unsafe { libc::getpid() }) + .into_result() + .map_err(JailerError::GetPid)?; + sid == ppid + } + }; + // Duplicate the current process. The child process will belong to the previously created // PID namespace. The current process will not be moved into the newly created namespace, // but its first child will assume the role of init(1) in the new namespace. let pid = clone(std::ptr::null_mut(), libc::CLONE_NEWPID)?; match pid { - 0 => Err(JailerError::Exec(self.exec_command(chroot_exec_file))), + 0 => { + if is_session_leader { + // SAFETY: Safe bacause it doesn't take any input parameters. + SyscallReturnCode(unsafe { libc::setsid() }) + .into_empty_result() + .map_err(JailerError::SetSid)?; + } + Err(JailerError::Exec(self.exec_command(chroot_exec_file))) + } child_pid => { // Save the PID of the process running the exec file provided // inside .pid file. @@ -404,18 +425,17 @@ impl Env { fn mknod_and_own_dev( &self, - dev_path_str: &'static str, + dev_path: &CStr, dev_major: u32, dev_minor: u32, ) -> Result<(), JailerError> { - let dev_path = CString::new(dev_path_str).unwrap(); // As per sysstat.h: // S_IFCHR -> character special device // S_IRUSR -> read permission, owner // S_IWUSR -> write permission, owner // See www.kernel.org/doc/Documentation/networking/tuntap.txt, 'Configuration' chapter for // more clarity. - // SAFETY: This is safe because dev_path is CString, and hence null-terminated. + // SAFETY: This is safe because dev_path is CStr, and hence null-terminated. SyscallReturnCode(unsafe { libc::mknod( dev_path.as_ptr(), @@ -424,7 +444,7 @@ impl Env { ) }) .into_empty_result() - .map_err(|err| JailerError::MknodDev(err, dev_path_str.to_owned()))?; + .map_err(|err| JailerError::MknodDev(err, dev_path.to_str().unwrap().to_owned()))?; // SAFETY: This is safe because dev_path is CStr, and hence null-terminated. SyscallReturnCode(unsafe { libc::chown(dev_path.as_ptr(), self.uid(), self.gid()) }) @@ -443,12 +463,8 @@ impl Env { .map_err(|err| JailerError::Chmod(folder_path.to_owned(), err))?; let c_path = CString::new(folder_path.to_str().unwrap()).unwrap(); - #[cfg(target_arch = "x86_64")] - let folder_bytes_ptr = c_path.as_ptr().cast::(); - #[cfg(target_arch = "aarch64")] - let folder_bytes_ptr = c_path.as_ptr(); // SAFETY: This is safe because folder was checked for a null-terminator. - SyscallReturnCode(unsafe { libc::chown(folder_bytes_ptr, self.uid(), self.gid()) }) + SyscallReturnCode(unsafe { libc::chown(c_path.as_ptr(), self.uid(), self.gid()) }) .into_empty_result() .map_err(|err| JailerError::ChangeFileOwner(folder_path.to_owned(), err)) } @@ -458,12 +474,7 @@ impl Env { .exec_file_path .file_name() .ok_or_else(|| JailerError::ExtractFileName(self.exec_file_path.clone()))?; - // We do a quick push here to get the global path of the executable inside the chroot, - // without having to create a new PathBuf. We'll then do a pop to revert to the actual - // chroot_dir right after the copy. - // TODO: just now wondering ... is doing a push()/pop() thing better than just creating - // a new PathBuf, with something like chroot_dir.join(exec_file_name) ?! - self.chroot_dir.push(exec_file_name); + let jailer_exec_file_path = self.chroot_dir.join(exec_file_name); // We do a copy instead of a hard-link for 2 reasons // 1. hard-linking is not possible if the file is in another device @@ -471,13 +482,15 @@ impl Env { // Firecracker binary (like the executable .text section), this latter part is not // desirable in Firecracker's threat model. Copying prevents 2 Firecracker processes from // sharing memory. - fs::copy(&self.exec_file_path, &self.chroot_dir).map_err(|err| { - JailerError::Copy(self.exec_file_path.clone(), self.chroot_dir.clone(), err) + fs::copy(&self.exec_file_path, &jailer_exec_file_path).map_err(|err| { + JailerError::Copy( + self.exec_file_path.clone(), + jailer_exec_file_path.clone(), + err, + ) })?; - // Pop exec_file_name. - self.chroot_dir.pop(); - Ok(exec_file_name.to_os_string()) + Ok(exec_file_name.to_owned()) } fn join_netns(path: &str) -> Result<(), JailerError> { @@ -495,7 +508,10 @@ impl Env { Command::new(chroot_exec_file) .args(["--id", &self.id]) .args(["--start-time-us", &self.start_time_us.to_string()]) - .args(["--start-time-cpu-us", &self.start_time_cpu_us.to_string()]) + .args([ + "--start-time-cpu-us", + &get_time_us(ClockType::ProcessCpu).to_string(), + ]) .args(["--parent-cpu-time-us", &self.jailer_cpu_time_us.to_string()]) .stdin(Stdio::inherit()) .stdout(Stdio::inherit()) @@ -551,17 +567,20 @@ impl Env { let host_cache_file = host_path.join(entry); let jailer_cache_file = jailer_path.join(entry); - let line = readln_special(&host_cache_file)?; - writeln_special(&jailer_cache_file, line)?; - - // We now change the permissions. - let dest_path_cstr = to_cstring(&jailer_cache_file)?; - // SAFETY: Safe because dest_path_cstr is null-terminated. - SyscallReturnCode(unsafe { - libc::chown(dest_path_cstr.as_ptr(), self.uid(), self.gid()) - }) - .into_empty_result() - .map_err(|err| JailerError::ChangeFileOwner(jailer_cache_file.to_owned(), err))?; + if let Ok(line) = readln_special(&host_cache_file) { + writeln_special(&jailer_cache_file, line)?; + + // We now change the permissions. + let dest_path_cstr = to_cstring(&jailer_cache_file)?; + // SAFETY: Safe because dest_path_cstr is null-terminated. + SyscallReturnCode(unsafe { + libc::chown(dest_path_cstr.as_ptr(), self.uid(), self.gid()) + }) + .into_empty_result() + .map_err(|err| { + JailerError::ChangeFileOwner(jailer_cache_file.to_owned(), err) + })?; + } } } Ok(()) @@ -639,14 +658,14 @@ impl Env { // $: mknod $dev_net_tun_path c 10 200 // www.kernel.org/doc/Documentation/networking/tuntap.txt specifies 10 and 200 as the major // and minor for the /dev/net/tun device. - self.mknod_and_own_dev(DEV_NET_TUN_WITH_NUL, DEV_NET_TUN_MAJOR, DEV_NET_TUN_MINOR)?; + self.mknod_and_own_dev(DEV_NET_TUN, DEV_NET_TUN_MAJOR, DEV_NET_TUN_MINOR)?; // Do the same for /dev/kvm with (major, minor) = (10, 232). - self.mknod_and_own_dev(DEV_KVM_WITH_NUL, DEV_KVM_MAJOR, DEV_KVM_MINOR)?; + self.mknod_and_own_dev(DEV_KVM, DEV_KVM_MAJOR, DEV_KVM_MINOR)?; // And for /dev/urandom with (major, minor) = (1, 9). // If the device is not accessible on the host, output a warning to inform user that MMDS // version 2 will not be available to use. let _ = self - .mknod_and_own_dev(DEV_URANDOM_WITH_NUL, DEV_URANDOM_MAJOR, DEV_URANDOM_MINOR) + .mknod_and_own_dev(DEV_URANDOM, DEV_URANDOM_MAJOR, DEV_URANDOM_MINOR) .map_err(|err| { println!( "Warning! Could not create /dev/urandom device inside jailer: {}.", @@ -661,11 +680,10 @@ impl Env { self.mknod_and_own_dev(DEV_UFFD_PATH, DEV_UFFD_MAJOR, minor)?; } + self.jailer_cpu_time_us = get_time_us(ClockType::ProcessCpu) - self.start_time_cpu_us; + // Daemonize before exec, if so required (when the dev_null variable != None). if let Some(dev_null) = dev_null { - // Meter CPU usage before fork() - self.jailer_cpu_time_us = get_time_us(ClockType::ProcessCpu); - // We follow the double fork method to daemonize the jailer referring to // https://0xjet.github.io/3OHA/2022/04/11/post.html // setsid() will fail if the calling process is a process group leader. @@ -688,7 +706,7 @@ impl Env { .into_empty_result() .map_err(JailerError::SetSid)?; - // Meter CPU usage before fork() + // Meter CPU usage after first fork() self.jailer_cpu_time_us += get_time_us(ClockType::ProcessCpu); // Daemons should not have controlling terminals. @@ -712,12 +730,10 @@ impl Env { dup2(dev_null.as_raw_fd(), STDIN_FILENO)?; dup2(dev_null.as_raw_fd(), STDOUT_FILENO)?; dup2(dev_null.as_raw_fd(), STDERR_FILENO)?; - } - // Compute jailer's total CPU time up to the current time. - self.jailer_cpu_time_us += get_time_us(ClockType::ProcessCpu) - self.start_time_cpu_us; - // Reset process start time. - self.start_time_cpu_us = 0; + // Meter CPU usage after second fork() + self.jailer_cpu_time_us += get_time_us(ClockType::ProcessCpu); + } // If specified, exec the provided binary into a new PID namespace. if self.new_pid_ns { @@ -838,14 +854,6 @@ mod tests { arg_vec } - fn get_major(dev: u64) -> u32 { - unsafe { libc::major(dev) } - } - - fn get_minor(dev: u64) -> u32 { - unsafe { libc::minor(dev) } - } - fn create_env(mock_proc_mounts: &str) -> Env { // Create a standard environment. let arg_parser = build_arg_parser(); @@ -1095,17 +1103,17 @@ mod tests { // process management; it can't be isolated from side effects. } - fn ensure_mknod_and_own_dev(env: &Env, dev_path: &'static str, major: u32, minor: u32) { + fn ensure_mknod_and_own_dev(env: &Env, dev_path: &CStr, major: u32, minor: u32) { use std::os::unix::fs::FileTypeExt; // Create a new device node. env.mknod_and_own_dev(dev_path, major, minor).unwrap(); // Ensure device's properties. - let metadata = fs::metadata(dev_path).unwrap(); + let metadata = fs::metadata(dev_path.to_str().unwrap()).unwrap(); assert!(metadata.file_type().is_char_device()); - assert_eq!(get_major(metadata.st_rdev()), major); - assert_eq!(get_minor(metadata.st_rdev()), minor); + assert_eq!(libc::major(metadata.st_rdev()), major); + assert_eq!(libc::minor(metadata.st_rdev()), minor); assert_eq!( metadata.permissions().mode(), libc::S_IFCHR | libc::S_IRUSR | libc::S_IWUSR @@ -1119,7 +1127,7 @@ mod tests { ), format!( "Failed to create {} via mknod inside the jail: File exists (os error 17)", - dev_path + dev_path.to_str().unwrap() ) ); } @@ -1131,25 +1139,25 @@ mod tests { let env = create_env(mock_cgroups.proc_mounts_path.as_str()); // Ensure device nodes are created with correct major/minor numbers and permissions. - let mut dev_infos: Vec<(&str, u32, u32)> = vec![ - ("/dev/net/tun-test", DEV_NET_TUN_MAJOR, DEV_NET_TUN_MINOR), - ("/dev/kvm-test", DEV_KVM_MAJOR, DEV_KVM_MINOR), + let mut dev_infos: Vec<(&CStr, u32, u32)> = vec![ + (c"/dev/net/tun-test", DEV_NET_TUN_MAJOR, DEV_NET_TUN_MINOR), + (c"/dev/kvm-test", DEV_KVM_MAJOR, DEV_KVM_MINOR), ]; if let Some(uffd_dev_minor) = env.uffd_dev_minor { - dev_infos.push(("/dev/userfaultfd-test", DEV_UFFD_MAJOR, uffd_dev_minor)); + dev_infos.push((c"/dev/userfaultfd-test", DEV_UFFD_MAJOR, uffd_dev_minor)); } for (dev, major, minor) in dev_infos { // Checking this just to be super sure there's no file at `dev_str` path (though // it shouldn't be as we deleted it at the end of the previous test run). - if Path::new(dev).exists() { - fs::remove_file(dev).unwrap(); + if Path::new(dev.to_str().unwrap()).exists() { + fs::remove_file(dev.to_str().unwrap()).unwrap(); } ensure_mknod_and_own_dev(&env, dev, major, minor); // Remove the device node. - fs::remove_file(dev).expect("Could not remove file."); + fs::remove_file(dev.to_str().unwrap()).expect("Could not remove file."); } } @@ -1159,7 +1167,7 @@ mod tests { mock_cgroups.add_v1_mounts().unwrap(); let env = create_env(mock_cgroups.proc_mounts_path.as_str()); - if !Path::new(DEV_UFFD_PATH).exists() { + if !Path::new(DEV_UFFD_PATH.to_str().unwrap()).exists() { assert_eq!(env.uffd_dev_minor, None); } else { assert!(env.uffd_dev_minor.is_some()); diff --git a/src/jailer/src/main.rs b/src/jailer/src/main.rs index 4e00c14762a..721531e49ba 100644 --- a/src/jailer/src/main.rs +++ b/src/jailer/src/main.rs @@ -8,7 +8,7 @@ use std::{env as p_env, fs, io}; use env::PROC_MOUNTS; use utils::arg_parser::{ArgParser, Argument, UtilsArgParserError as ParsingError}; -use utils::time::{get_time_us, ClockType}; +use utils::time::{ClockType, get_time_us}; use utils::validators; use vmm_sys_util::syscall::SyscallReturnCode; @@ -85,6 +85,10 @@ pub enum JailerError { FromBytesWithNul(std::ffi::FromBytesWithNulError), #[error("Failed to get flags from fd: {0}")] GetOldFdFlags(io::Error), + #[error("Failed to get PID (getpid): {0}")] + GetPid(io::Error), + #[error("Failed to get SID (getsid): {0}")] + GetSid(io::Error), #[error("Invalid gid: {0}")] Gid(String), #[error("Invalid instance ID: {0}")] @@ -284,7 +288,10 @@ fn clean_env_vars() { // the parent process so there are no leaks // inside the jailer environment for (key, _) in p_env::vars() { - p_env::remove_var(key); + // SAFETY: the function is safe to call in a single-threaded program + unsafe { + p_env::remove_var(key); + } } } @@ -301,6 +308,7 @@ pub fn to_cstring + Debug>(path: T) -> Result Result<(), JailerError> { let result = main_exec(); if let Err(e) = result { @@ -418,7 +426,10 @@ mod tests { // Set environment variables for env_var in env_vars.iter() { - env::set_var(env_var, "0"); + // SAFETY: the function is safe to call in a single-threaded program + unsafe { + env::set_var(env_var, "0"); + } } // Cleanup the environment diff --git a/src/log-instrument-macros/Cargo.toml b/src/log-instrument-macros/Cargo.toml index fd25f4ffe08..0d2c564f668 100644 --- a/src/log-instrument-macros/Cargo.toml +++ b/src/log-instrument-macros/Cargo.toml @@ -2,7 +2,7 @@ name = "log-instrument-macros" version = "0.1.0" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" description = "Offers an attribute procedural macro that adds `log::trace!` events at the start and end of attributed functions." license = "Apache-2.0" @@ -11,9 +11,9 @@ proc-macro = true bench = false [dependencies] -proc-macro2 = "1.0.89" -quote = "1.0.37" -syn = { version = "2.0.85", features = ["full", "extra-traits"] } +proc-macro2 = "1.0.94" +quote = "1.0.40" +syn = { version = "2.0.100", features = ["full", "extra-traits"] } [lints] workspace = true diff --git a/src/log-instrument/Cargo.toml b/src/log-instrument/Cargo.toml index e418a7cf886..42489427841 100644 --- a/src/log-instrument/Cargo.toml +++ b/src/log-instrument/Cargo.toml @@ -2,7 +2,7 @@ name = "log-instrument" version = "0.3.0" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" description = "Offers an attribute procedural macro that adds `log::trace!` events at the start and end of attributed functions." license = "Apache-2.0" @@ -28,11 +28,11 @@ name = "five" name = "six" [dependencies] -log = "0.4.22" +log = "0.4.27" log-instrument-macros = { path = "../log-instrument-macros" } [dev-dependencies] -env_logger = "0.11.5" +env_logger = "0.11.8" [lints] workspace = true diff --git a/src/log-instrument/examples/five.rs b/src/log-instrument/examples/five.rs index e43064c18b5..07aa0951935 100644 --- a/src/log-instrument/examples/five.rs +++ b/src/log-instrument/examples/five.rs @@ -3,7 +3,7 @@ #![warn(clippy::pedantic)] -use log::{debug, info, warn, LevelFilter}; +use log::{LevelFilter, debug, info, warn}; fn main() { env_logger::builder() diff --git a/src/rebase-snap/Cargo.toml b/src/rebase-snap/Cargo.toml index e962c912668..2e333208a75 100644 --- a/src/rebase-snap/Cargo.toml +++ b/src/rebase-snap/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "rebase-snap" -version = "1.10.0-dev" +version = "1.12.0-dev" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" license = "Apache-2.0" [[bin]] @@ -11,9 +11,9 @@ bench = false [dependencies] displaydoc = "0.2.5" -libc = "0.2.161" +libc = "0.2.171" log-instrument = { path = "../log-instrument", optional = true } -thiserror = "1.0.67" +thiserror = "2.0.12" vmm-sys-util = "0.12.1" utils = { path = "../utils" } diff --git a/src/seccompiler/Cargo.toml b/src/seccompiler/Cargo.toml index 5854f9fb67d..f9c06872fe3 100644 --- a/src/seccompiler/Cargo.toml +++ b/src/seccompiler/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "seccompiler" -version = "1.10.0-dev" +version = "1.12.0-dev" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" description = "Program that compiles multi-threaded seccomp-bpf filters expressed as JSON into raw BPF programs, serializing them and outputting them to a file." homepage = "https://firecracker-microvm.github.io/" license = "Apache-2.0" @@ -12,25 +12,18 @@ bench = false [[bin]] name = "seccompiler-bin" -path = "src/seccompiler_bin.rs" +path = "src/bin.rs" bench = false [dependencies] -bincode = "1.2.1" +bincode = { version = "2.0.1", features = ["serde"] } +clap = { version = "4.5.35", features = ["derive", "string"] } displaydoc = "0.2.5" -libc = "0.2.161" -log-instrument = { path = "../log-instrument", optional = true } -serde = { version = "1.0.214", features = ["derive"] } -serde_json = "1.0.132" -thiserror = "1.0.67" - -utils = { path = "../utils" } - -[dev-dependencies] -vmm-sys-util = "0.12.1" - -[features] -tracing = ["log-instrument", "utils/tracing"] +libc = "0.2.171" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" +thiserror = "2.0.12" +zerocopy = { version = "0.8.24" } [lints] workspace = true diff --git a/src/seccompiler/build.rs b/src/seccompiler/build.rs new file mode 100644 index 00000000000..d0d2a30e39e --- /dev/null +++ b/src/seccompiler/build.rs @@ -0,0 +1,7 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +fn main() { + println!("cargo::rustc-link-search=/usr/local/lib"); + println!("cargo::rustc-link-lib=seccomp"); +} diff --git a/src/seccompiler/src/backend.rs b/src/seccompiler/src/backend.rs deleted file mode 100644 index cc4b1d4c9e3..00000000000 --- a/src/seccompiler/src/backend.rs +++ /dev/null @@ -1,1814 +0,0 @@ -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -#![cfg(target_endian = "little")] -//! This module defines the data structures used for the intermmediate representation (IR), -//! as well as the logic for compiling the filter into BPF code, the final form of the filter. - -use std::collections::BTreeMap; -use std::convert::{Into, TryFrom, TryInto}; - -use serde::{Deserialize, Deserializer}; - -use crate::common::{sock_filter, BpfProgram, BPF_MAX_LEN}; - -// BPF Instruction classes. -// See /usr/include/linux/bpf_common.h . -const BPF_LD: u16 = 0x00; -const BPF_ALU: u16 = 0x04; -const BPF_JMP: u16 = 0x05; -const BPF_RET: u16 = 0x06; - -// BPF ld/ldx fields. -// See /usr/include/linux/bpf_common.h . -const BPF_W: u16 = 0x00; -const BPF_ABS: u16 = 0x20; - -// BPF alu fields. -// See /usr/include/linux/bpf_common.h . -const BPF_AND: u16 = 0x50; - -// BPF jmp fields. -// See /usr/include/linux/bpf_common.h . -const BPF_JA: u16 = 0x00; -const BPF_JEQ: u16 = 0x10; -const BPF_JGT: u16 = 0x20; -const BPF_JGE: u16 = 0x30; -const BPF_K: u16 = 0x00; - -// Return codes for BPF programs. -// See /usr/include/linux/seccomp.h . -const SECCOMP_RET_ALLOW: u32 = 0x7fff_0000; -const SECCOMP_RET_ERRNO: u32 = 0x0005_0000; -const SECCOMP_RET_KILL_THREAD: u32 = 0x0000_0000; -const SECCOMP_RET_KILL_PROCESS: u32 = 0x8000_0000; -const SECCOMP_RET_LOG: u32 = 0x7ffc_0000; -const SECCOMP_RET_TRACE: u32 = 0x7ff0_0000; -const SECCOMP_RET_TRAP: u32 = 0x0003_0000; -const SECCOMP_RET_MASK: u32 = 0x0000_ffff; - -// Architecture identifier. -// See /usr/include/linux/audit.h . - -// Defined as: -// `#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)` -const AUDIT_ARCH_X86_64: u32 = 62 | 0x8000_0000 | 0x4000_0000; - -// Defined as: -// `#define AUDIT_ARCH_AARCH64 (EM_AARCH64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)` -const AUDIT_ARCH_AARCH64: u32 = 183 | 0x8000_0000 | 0x4000_0000; - -// The maximum number of a syscall argument. -// A syscall can have at most 6 arguments. -// Arguments are numbered from 0 to 5. -const ARG_NUMBER_MAX: u8 = 5; - -// The maximum number of BPF statements that a condition will be translated into. -const CONDITION_MAX_LEN: u8 = 6; - -// `struct seccomp_data` offsets and sizes of fields in bytes: -// -// ```c -// struct seccomp_data { -// int nr; -// __u32 arch; -// __u64 instruction_pointer; -// __u64 args[6]; -// }; -// ``` -const SECCOMP_DATA_NR_OFFSET: u8 = 0; -const SECCOMP_DATA_ARGS_OFFSET: u8 = 16; -const SECCOMP_DATA_ARG_SIZE: u8 = 8; - -/// Dummy placeholder type for a JSON comment. Holds no value. -#[derive(PartialEq, Debug, Clone)] -pub struct Comment; - -impl<'de> Deserialize<'de> for Comment { - fn deserialize(_deserializer: D) -> std::result::Result - where - D: Deserializer<'de>, - { - String::deserialize(_deserializer)?; - - Ok(Comment {}) - } -} - -/// Seccomp filter errors. -#[derive(Debug, PartialEq, thiserror::Error, displaydoc::Display)] -pub enum FilterError { - /// The seccomp rules vector is empty. - EmptyRulesVector, - /// The seccomp filter contains too many BPF instructions. - FilterTooLarge, - /// The seccomp rule contains an invalid argument number. - InvalidArgumentNumber, - /// {0} - Arch(TargetArchError), - /// Syscall {0} has conflicting rules. - ConflictingRules(i64), -} - -/// Supported target architectures. -#[allow(non_camel_case_types)] -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum TargetArch { - /// x86_64 arch - x86_64, - /// aarch64 arch - aarch64, -} - -/// Errors related to target arch. -#[derive(Debug, PartialEq, thiserror::Error, displaydoc::Display)] -pub enum TargetArchError { - /// Invalid target arch string: {0} - InvalidString(String), -} - -impl TargetArch { - /// Get the arch audit value. - fn get_audit_value(self) -> u32 { - match self { - TargetArch::x86_64 => AUDIT_ARCH_X86_64, - TargetArch::aarch64 => AUDIT_ARCH_AARCH64, - } - } - - /// Get the string representation. - fn to_string(self) -> &'static str { - match self { - TargetArch::x86_64 => "x86_64", - TargetArch::aarch64 => "aarch64", - } - } -} - -impl TryInto for &str { - type Error = TargetArchError; - fn try_into(self) -> std::result::Result { - match self.to_lowercase().as_str() { - "x86_64" => Ok(TargetArch::x86_64), - "aarch64" => Ok(TargetArch::aarch64), - _ => Err(TargetArchError::InvalidString(self.to_string())), - } - } -} - -impl From for &str { - fn from(target_arch: TargetArch) -> Self { - target_arch.to_string() - } -} - -/// Comparison to perform when matching a condition. -#[derive(Clone, Debug, Deserialize, PartialEq)] -#[serde(rename_all = "snake_case")] -pub enum SeccompCmpOp { - /// Argument value is equal to the specified value. - Eq, - /// Argument value is greater than or equal to the specified value. - Ge, - /// Argument value is greater than specified value. - Gt, - /// Argument value is less than or equal to the specified value. - Le, - /// Argument value is less than specified value. - Lt, - /// Masked bits of argument value are equal to masked bits of specified value. - MaskedEq(u64), - /// Argument value is not equal to specified value. - Ne, -} - -/// Seccomp argument value length. -#[derive(Clone, Debug, Deserialize, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum SeccompCmpArgLen { - /// Argument value length is 4 bytes. - Dword, - /// Argument value length is 8 bytes. - Qword, -} - -/// Condition that syscall must match in order to satisfy a rule. -#[derive(Clone, Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct SeccompCondition { - /// Index of the argument that is to be compared. - #[serde(rename = "index")] - arg_number: u8, - /// Length of the argument value that is to be compared. - #[serde(rename = "type")] - arg_len: SeccompCmpArgLen, - /// Comparison to perform. - #[serde(rename = "op")] - operator: SeccompCmpOp, - /// The value that will be compared with the argument value. - #[serde(rename = "val")] - value: u64, - /// Optional empty value, represents a `comment` property in the JSON file. - comment: Option, -} - -/// Actions that `seccomp` can apply to process calling a syscall. -#[derive(Clone, Debug, PartialEq, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum SeccompAction { - /// Allows syscall. - Allow, - /// Returns from syscall with specified error number. - Errno(u32), - /// Kills calling thread. - KillThread, - /// Kills calling process. - KillProcess, - /// Same as allow but logs call. - Log, - /// Notifies tracing process of the caller with respective number. - Trace(u32), - /// Sends `SIGSYS` to the calling process. - Trap, -} - -/// Rule that `seccomp` attempts to match for a syscall. -/// -/// If all conditions match then rule gets matched. -/// The action of the first rule that matches will be applied to the calling process. -/// If no rule matches the default action is applied. -#[derive(Clone, Debug, PartialEq)] -pub struct SeccompRule { - /// Conditions of rule that need to match in order for the rule to get matched. - conditions: Vec, - /// Action applied to calling process if rule gets matched. - action: SeccompAction, -} - -/// Type that associates the syscall number to its SeccompRules. -pub type SeccompRuleMap = BTreeMap>; - -/// Filter containing rules assigned to syscall numbers. -#[derive(Clone, Debug, PartialEq)] -pub struct SeccompFilter { - /// Map of syscall numbers and corresponding rule chains. - rules: SeccompRuleMap, - /// Default action to apply to syscall numbers that do not exist in the hash map. - default_action: SeccompAction, - /// Target architecture of the generated BPF filter. - target_arch: TargetArch, -} - -impl SeccompCondition { - /// Validates the SeccompCondition data - pub fn validate(&self) -> Result<(), FilterError> { - // Checks that the given argument number is valid. - if self.arg_number > ARG_NUMBER_MAX { - return Err(FilterError::InvalidArgumentNumber); - } - - Ok(()) - } - - /// Splits the [`SeccompCondition`] into 32 bit chunks and offsets. - /// - /// Returns most significant half, least significant half of the `value` field of - /// [`SeccompCondition`], as well as the offsets of the most significant and least significant - /// half of the argument specified by `arg_number` relative to `struct seccomp_data` passed to - /// the BPF program by the kernel. - /// - /// [`SeccompCondition`]: struct.SeccompCondition.html - fn value_segments(&self) -> (u32, u32, u8, u8) { - // Splits the specified value into its most significant and least significant halves. - let (msb, lsb) = ((self.value >> 32) as u32, (self.value & 0xFFFFFFFF) as u32); - - // Offset to the argument specified by `arg_number`. - // Cannot overflow because the value will be at most 16 + 6 * 8 = 64. - let arg_offset = SECCOMP_DATA_ARGS_OFFSET + self.arg_number * SECCOMP_DATA_ARG_SIZE; - - // Extracts offsets of most significant and least significant halves of argument. - // Addition cannot overflow because it's at most `arg_offset` + 4 = 68. - let (msb_offset, lsb_offset) = { (arg_offset + SECCOMP_DATA_ARG_SIZE / 2, arg_offset) }; - - (msb, lsb, msb_offset, lsb_offset) - } - - /// Translates the `eq` (equal) condition into BPF statements. - /// - /// # Arguments - /// - /// * `offset` - The given jump offset to the start of the next rule. - /// - /// The jump is performed if the condition fails and thus the current rule does not match so - /// `seccomp` tries to match the next rule by jumping out of the current rule. - /// - /// In case the condition is part of the last rule, the jump offset is to the default action of - /// respective filter. - /// - /// The most significant and least significant halves of the argument value are compared - /// separately since the BPF operand and accumulator are 4 bytes whereas an argument value is 8. - fn into_eq_bpf(self, offset: u8) -> Vec { - let (msb, lsb, msb_offset, lsb_offset) = self.value_segments(); - - let mut bpf = match self.arg_len { - SeccompCmpArgLen::Dword => vec![], - SeccompCmpArgLen::Qword => vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(msb_offset)), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, msb, 0, offset + 2), - ], - }; - - bpf.append(&mut vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(lsb_offset)), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, lsb, 0, offset), - ]); - bpf - } - - /// Translates the `ge` (greater than or equal) condition into BPF statements. - /// - /// # Arguments - /// - /// * `offset` - The given jump offset to the start of the next rule. - fn into_ge_bpf(self, offset: u8) -> Vec { - let (msb, lsb, msb_offset, lsb_offset) = self.value_segments(); - - let mut bpf = match self.arg_len { - SeccompCmpArgLen::Dword => vec![], - SeccompCmpArgLen::Qword => vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(msb_offset)), - BPF_JUMP(BPF_JMP + BPF_JGT + BPF_K, msb, 3, 0), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, msb, 0, offset + 2), - ], - }; - - bpf.append(&mut vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(lsb_offset)), - BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, lsb, 0, offset), - ]); - bpf - } - - /// Translates the `gt` (greater than) condition into BPF statements. - /// - /// # Arguments - /// - /// * `offset` - The given jump offset to the start of the next rule. - fn into_gt_bpf(self, offset: u8) -> Vec { - let (msb, lsb, msb_offset, lsb_offset) = self.value_segments(); - - let mut bpf = match self.arg_len { - SeccompCmpArgLen::Dword => vec![], - SeccompCmpArgLen::Qword => vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(msb_offset)), - BPF_JUMP(BPF_JMP + BPF_JGT + BPF_K, msb, 3, 0), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, msb, 0, offset + 2), - ], - }; - - bpf.append(&mut vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(lsb_offset)), - BPF_JUMP(BPF_JMP + BPF_JGT + BPF_K, lsb, 0, offset), - ]); - bpf - } - - /// Translates the `le` (less than or equal) condition into BPF statements. - /// - /// # Arguments - /// - /// * `offset` - The given jump offset to the start of the next rule. - fn into_le_bpf(self, offset: u8) -> Vec { - let (msb, lsb, msb_offset, lsb_offset) = self.value_segments(); - - let mut bpf = match self.arg_len { - SeccompCmpArgLen::Dword => vec![], - SeccompCmpArgLen::Qword => vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(msb_offset)), - BPF_JUMP(BPF_JMP + BPF_JGT + BPF_K, msb, offset + 3, 0), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, msb, 0, 2), - ], - }; - - bpf.append(&mut vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(lsb_offset)), - BPF_JUMP(BPF_JMP + BPF_JGT + BPF_K, lsb, offset, 0), - ]); - bpf - } - - /// Translates the `lt` (less than) condition into BPF statements. - /// - /// # Arguments - /// - /// * `offset` - The given jump offset to the start of the next rule. - fn into_lt_bpf(self, offset: u8) -> Vec { - let (msb, lsb, msb_offset, lsb_offset) = self.value_segments(); - - let mut bpf = match self.arg_len { - SeccompCmpArgLen::Dword => vec![], - SeccompCmpArgLen::Qword => vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(msb_offset)), - BPF_JUMP(BPF_JMP + BPF_JGT + BPF_K, msb, offset + 3, 0), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, msb, 0, 2), - ], - }; - - bpf.append(&mut vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(lsb_offset)), - BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, lsb, offset, 0), - ]); - bpf - } - - /// Translates the `masked_eq` (masked equal) condition into BPF statements. - /// - /// The `masked_eq` condition is `true` if the result of logical `AND` between the given value - /// and the mask is the value being compared against. - /// - /// # Arguments - /// - /// * `offset` - The given jump offset to the start of the next rule. - fn into_masked_eq_bpf(self, offset: u8, mask: u64) -> Vec { - let (_, _, msb_offset, lsb_offset) = self.value_segments(); - let masked_value = self.value & mask; - let (msb, lsb) = ( - (masked_value >> 32) as u32, - (masked_value & 0xFFFFFFFF) as u32, - ); - let (mask_msb, mask_lsb) = ((mask >> 32) as u32, (mask & 0xFFFFFFFF) as u32); - - let mut bpf = match self.arg_len { - SeccompCmpArgLen::Dword => vec![], - SeccompCmpArgLen::Qword => vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(msb_offset)), - BPF_STMT(BPF_ALU + BPF_AND + BPF_K, mask_msb), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, msb, 0, offset + 3), - ], - }; - - bpf.append(&mut vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(lsb_offset)), - BPF_STMT(BPF_ALU + BPF_AND + BPF_K, mask_lsb), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, lsb, 0, offset), - ]); - bpf - } - - /// Translates the `ne` (not equal) condition into BPF statements. - /// - /// # Arguments - /// - /// * `offset` - The given jump offset to the start of the next rule. - fn into_ne_bpf(self, offset: u8) -> Vec { - let (msb, lsb, msb_offset, lsb_offset) = self.value_segments(); - - let mut bpf = match self.arg_len { - SeccompCmpArgLen::Dword => vec![], - SeccompCmpArgLen::Qword => vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(msb_offset)), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, msb, 0, 2), - ], - }; - - bpf.append(&mut vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, u32::from(lsb_offset)), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, lsb, offset, 0), - ]); - bpf - } - - /// Translates the [`SeccompCondition`] into BPF statements. - /// - /// # Arguments - /// - /// * `offset` - The given jump offset to the start of the next rule. - /// - /// [`SeccompCondition`]: struct.SeccompCondition.html - fn into_bpf(self, offset: u8) -> Vec { - let result = match self.operator { - SeccompCmpOp::Eq => self.into_eq_bpf(offset), - SeccompCmpOp::Ge => self.into_ge_bpf(offset), - SeccompCmpOp::Gt => self.into_gt_bpf(offset), - SeccompCmpOp::Le => self.into_le_bpf(offset), - SeccompCmpOp::Lt => self.into_lt_bpf(offset), - SeccompCmpOp::MaskedEq(mask) => self.into_masked_eq_bpf(offset, mask), - SeccompCmpOp::Ne => self.into_ne_bpf(offset), - }; - - // Verifies that the `CONDITION_MAX_LEN` constant was properly updated. - assert!(result.len() <= CONDITION_MAX_LEN as usize); - - result - } -} - -impl From for u32 { - /// Return codes of the BPF program for each action. - /// - /// # Arguments - /// - /// * `action` - The [`SeccompAction`] that the kernel will take. - /// - /// [`SeccompAction`]: struct.SeccompAction.html - fn from(action: SeccompAction) -> Self { - match action { - SeccompAction::Allow => SECCOMP_RET_ALLOW, - SeccompAction::Errno(x) => SECCOMP_RET_ERRNO | (x & SECCOMP_RET_MASK), - SeccompAction::KillThread => SECCOMP_RET_KILL_THREAD, - SeccompAction::KillProcess => SECCOMP_RET_KILL_PROCESS, - SeccompAction::Log => SECCOMP_RET_LOG, - SeccompAction::Trace(x) => SECCOMP_RET_TRACE | (x & SECCOMP_RET_MASK), - SeccompAction::Trap => SECCOMP_RET_TRAP, - } - } -} - -impl SeccompRule { - /// Creates a new rule. Rules with 0 conditions always match. - /// - /// # Arguments - /// - /// * `conditions` - Vector of [`SeccompCondition`] that the syscall must match. - /// * `action` - Action taken if the syscall matches the conditions. See [`SeccompAction`]. - /// - /// [`SeccompCondition`]: struct.SeccompCondition.html - /// [`SeccompAction`]: struct.SeccompAction.html - pub fn new(conditions: Vec, action: SeccompAction) -> Self { - Self { conditions, action } - } - - /// Appends a condition of the rule to an accumulator. - /// - /// The length of the rule and offset to the next rule are updated. - /// - /// # Arguments - /// - /// * `condition` - The condition added to the rule. - /// * `accumulator` - Accumulator of BPF statements that compose the BPF program. - /// * `rule_len` - Number of conditions in the rule. - /// * `offset` - Offset (in number of BPF statements) to the next rule. - fn append_condition( - condition: SeccompCondition, - accumulator: &mut Vec>, - rule_len: &mut usize, - offset: &mut u8, - ) { - // Tries to detect whether prepending the current condition will produce an unjumpable - // offset (since BPF jumps are a maximum of 255 instructions, which is u8::MAX). - if offset.checked_add(CONDITION_MAX_LEN + 1).is_none() { - // If that is the case, three additional helper jumps are prepended and the offset - // is reset to 1. - // - // - The first jump continues the evaluation of the condition chain by jumping to the - // next condition or the action of the rule if the last condition was matched. - // - The second, jumps out of the rule, to the next rule or the default action of the - // filter in case of the last rule in the rule chain of a syscall. - // - The third jumps out of the rule chain of the syscall, to the rule chain of the next - // syscall number to be checked or the default action of the filter in the case of the - // last rule chain. - let helper_jumps = vec![ - BPF_STMT(BPF_JMP + BPF_JA, 2), - BPF_STMT(BPF_JMP + BPF_JA, u32::from(*offset) + 1), - BPF_STMT(BPF_JMP + BPF_JA, u32::from(*offset) + 1), - ]; - *rule_len += helper_jumps.len(); - accumulator.push(helper_jumps); - *offset = 1; - } - - let condition = condition.into_bpf(*offset); - *rule_len += condition.len(); - // Safe to unwrap since we checked that condition length is less than `CONDITION_MAX_LEN`. - *offset += u8::try_from(condition.len()).unwrap(); - accumulator.push(condition); - } -} - -impl From for BpfProgram { - /// Translates a rule into BPF statements. - /// - /// Each rule starts with 2 jump statements: - /// * The first jump enters the rule, attempting a match. - /// * The second jump points to the end of the rule chain for one syscall, into the rule chain - /// for the next syscall or the default action if the current syscall is the last one. It - /// essentially jumps out of the current rule chain. - fn from(rule: SeccompRule) -> Self { - // Rule is built backwards, last statement is the action of the rule. - // The offset to the next rule is 1. - let mut accumulator = - Vec::with_capacity(rule.conditions.len() * CONDITION_MAX_LEN as usize); - let mut rule_len = 1; - let mut offset = 1; - accumulator.push(vec![BPF_STMT(BPF_RET + BPF_K, u32::from(rule.action))]); - - // Conditions are translated into BPF statements and prepended to the rule. - rule.conditions.into_iter().for_each(|condition| { - SeccompRule::append_condition(condition, &mut accumulator, &mut rule_len, &mut offset) - }); - - // The two initial jump statements are prepended to the rule. - let rule_jumps = vec![ - BPF_STMT(BPF_JMP + BPF_JA, 1), - BPF_STMT(BPF_JMP + BPF_JA, u32::from(offset) + 1), - ]; - rule_len += rule_jumps.len(); - accumulator.push(rule_jumps); - - // Finally, builds the translated rule by consuming the accumulator. - let mut result = Vec::with_capacity(rule_len); - accumulator - .into_iter() - .rev() - .for_each(|mut instructions| result.append(&mut instructions)); - - result - } -} - -impl SeccompFilter { - /// Creates a new filter with a set of rules and a default action. - /// - /// # Arguments - /// - /// * `rules` - Map of syscall numbers and the rules that will be applied to each of them. - /// * `default_action` - Action taken for all syscalls that do not match any rule. - /// * `target_arch` - Target architecture of the generated BPF filter. - pub fn new( - rules: SeccompRuleMap, - default_action: SeccompAction, - target_arch: &str, - ) -> Result { - let instance = Self { - rules, - default_action, - target_arch: target_arch.try_into().map_err(FilterError::Arch)?, - }; - - instance.validate()?; - - Ok(instance) - } - - /// Performs semantic checks on the SeccompFilter. - fn validate(&self) -> Result<(), FilterError> { - for (syscall_number, syscall_rules) in self.rules.iter() { - // All inserted syscalls must have at least one rule, otherwise BPF code will break. - if syscall_rules.is_empty() { - return Err(FilterError::EmptyRulesVector); - } - - // Now check for conflicting rules. - // Match on the number of empty rules for the given syscall. - // An `empty rule` is a rule that doesn't have any argument checks. - match syscall_rules - .iter() - .filter(|rule| rule.conditions.is_empty()) - .count() - { - // If the syscall has an empty rule, it may only have that rule. - 1 if syscall_rules.len() > 1 => { - return Err(FilterError::ConflictingRules(*syscall_number)); - } - // This syscall only has the one rule, so is valid. - 1 if syscall_rules.len() <= 1 => {} - // The syscall has no empty rules. - 0 => {} - // For a greater than 1 number of empty rules, error out. - _ => { - return Err(FilterError::ConflictingRules(*syscall_number)); - } - } - } - - Ok(()) - } - - /// Appends a chain of rules to an accumulator, updating the length of the filter. - /// - /// # Arguments - /// - /// * `syscall_number` - The syscall to which the rules apply. - /// * `chain` - The chain of rules for the specified syscall. - /// * `default_action` - The action to be taken in none of the rules apply. - /// * `accumulator` - The expanding BPF program. - /// * `filter_len` - The size (in number of BPF statements) of the BPF program. This is limited - /// to 4096. If the limit is exceeded, the filter is invalidated. - fn append_syscall_chain( - syscall_number: i64, - chain: Vec, - default_action: u32, - accumulator: &mut Vec>, - filter_len: &mut usize, - ) -> Result<(), FilterError> { - // The rules of the chain are translated into BPF statements. - let chain: Vec<_> = chain.into_iter().map(SeccompRule::into).collect(); - let chain_len: usize = chain.iter().map(std::vec::Vec::len).sum(); - - // The chain starts with a comparison checking the loaded syscall number against the - // syscall number of the chain. - let mut built_syscall = Vec::with_capacity(1 + chain_len + 1); - built_syscall.push(BPF_JUMP( - BPF_JMP + BPF_JEQ + BPF_K, - u32::try_from(syscall_number).unwrap(), - 0, - 1, - )); - - // The rules of the chain are appended. - chain - .into_iter() - .for_each(|mut rule| built_syscall.append(&mut rule)); - - // The default action is appended, if the syscall number comparison matched and then all - // rules fail to match, the default action is reached. - built_syscall.push(BPF_STMT(BPF_RET + BPF_K, default_action)); - - // The chain is appended to the result. - *filter_len += built_syscall.len(); - accumulator.push(built_syscall); - - // BPF programs are limited to 4096 statements. - if *filter_len >= usize::from(BPF_MAX_LEN) { - return Err(FilterError::FilterTooLarge); - } - - Ok(()) - } -} - -impl TryInto for SeccompFilter { - type Error = FilterError; - fn try_into(self) -> Result { - // Initialize the result with the precursory architecture check. - let mut result = VALIDATE_ARCHITECTURE(self.target_arch); - - // If no rules are set up, the filter will always return the default action, - // so let's short-circuit the function. - if self.rules.is_empty() { - result.extend(vec![BPF_STMT( - BPF_RET + BPF_K, - u32::from(self.default_action), - )]); - - return Ok(result); - } - - // The called syscall number is loaded. - let mut accumulator = Vec::with_capacity(1); - let mut filter_len = 1; - accumulator.push(EXAMINE_SYSCALL()); - - // Orders syscalls by priority, the highest number represents the highest priority. - let mut iter = self.rules.into_iter(); - - // For each syscall adds its rule chain to the filter. - let default_action = u32::from(self.default_action); - iter.try_for_each(|(syscall_number, chain)| { - SeccompFilter::append_syscall_chain( - syscall_number, - chain, - default_action, - &mut accumulator, - &mut filter_len, - ) - })?; - - // The default action is once again appended, it is reached if all syscall number - // comparisons fail. - filter_len += 1; - accumulator.push(vec![BPF_STMT(BPF_RET + BPF_K, default_action)]); - - // Finally, builds the translated filter by consuming the accumulator. - result.reserve(filter_len); - accumulator - .into_iter() - .for_each(|mut instructions| result.append(&mut instructions)); - - if result.len() >= usize::from(BPF_MAX_LEN) { - return Err(FilterError::FilterTooLarge); - } - - Ok(result) - } -} - -/// Builds a `jump` BPF instruction. -/// -/// # Arguments -/// -/// * `code` - The operation code. -/// * `jt` - The jump offset in case the operation returns `true`. -/// * `jf` - The jump offset in case the operation returns `false`. -/// * `k` - The operand. -#[allow(non_snake_case)] -#[inline(always)] -fn BPF_JUMP(code: u16, k: u32, jt: u8, jf: u8) -> sock_filter { - sock_filter { code, jt, jf, k } -} - -/// Builds a "statement" BPF instruction. -/// -/// # Arguments -/// -/// * `code` - The operation code. -/// * `k` - The operand. -#[allow(non_snake_case)] -#[inline(always)] -fn BPF_STMT(code: u16, k: u32) -> sock_filter { - sock_filter { - code, - jt: 0, - jf: 0, - k, - } -} - -/// Builds a sequence of BPF instructions that validate the underlying architecture. -#[allow(non_snake_case)] -#[inline(always)] -fn VALIDATE_ARCHITECTURE(target_arch: TargetArch) -> Vec { - let audit_arch_value = target_arch.get_audit_value(); - vec![ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 4), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, audit_arch_value, 1, 0), - BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS), - ] -} - -/// Builds a sequence of BPF instructions that are followed by syscall examination. -#[allow(non_snake_case)] -#[inline(always)] -fn EXAMINE_SYSCALL() -> Vec { - vec![BPF_STMT( - BPF_LD + BPF_W + BPF_ABS, - u32::from(SECCOMP_DATA_NR_OFFSET), - )] -} - -#[cfg(test)] -mod tests { - #![allow(clippy::undocumented_unsafe_blocks)] - use std::env::consts::ARCH; - use std::thread; - - use super::SeccompCmpOp::*; - use super::{SeccompCmpArgLen as ArgLen, SeccompCondition as Cond, *}; - - // BPF structure definition for filter array. - // See /usr/include/linux/filter.h . - #[repr(C)] - struct sock_fprog { - pub len: ::std::os::raw::c_ushort, - pub filter: *const sock_filter, - } - - // Builds the (syscall, rules) tuple for allowing a syscall with certain arguments. - fn allow_syscall_if(syscall_number: i64, rules: Vec) -> (i64, Vec) { - (syscall_number, rules) - } - - impl SeccompCondition { - // Creates a new `SeccompCondition`. - pub fn new( - arg_number: u8, - arg_len: SeccompCmpArgLen, - operator: SeccompCmpOp, - value: u64, - ) -> Result { - let instance = Self { - arg_number, - arg_len, - operator, - value, - comment: None, - }; - - instance.validate().map(|_| Ok(instance))? - } - } - - // The type of the `req` parameter is different for the `musl` library. This will enable - // successful build for other non-musl libraries. - #[cfg(target_env = "musl")] - type IoctlRequest = i32; - #[cfg(not(target_env = "musl"))] - type IoctlRequest = u64; - - // We use KVM_GET_PIT2 as the second parameter for ioctl syscalls in some unit tests - // because it's a corner case. More details - // [here](https://github.com/firecracker-microvm/firecracker/issues/1206) - const KVM_GET_PIT2: u64 = 0x8070_ae9f; - const KVM_GET_PIT2_MSB: u64 = 0x0000_ae9f; - const KVM_GET_PIT2_LSB: u64 = 0x8070_0000; - - const EXTRA_SYSCALLS: [i64; 6] = [ - libc::SYS_rt_sigprocmask, - libc::SYS_sigaltstack, - libc::SYS_munmap, - libc::SYS_exit, - libc::SYS_rt_sigreturn, - libc::SYS_futex, - ]; - - fn install_filter(bpf_filter: BpfProgram) { - unsafe { - { - let rc = libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); - assert_eq!(rc, 0); - } - let bpf_prog = sock_fprog { - len: u16::try_from(bpf_filter.len()).unwrap(), - filter: bpf_filter.as_ptr(), - }; - let bpf_prog_ptr = &bpf_prog as *const sock_fprog; - { - let rc = libc::prctl( - libc::PR_SET_SECCOMP, - libc::SECCOMP_MODE_FILTER, - bpf_prog_ptr, - ); - assert_eq!(rc, 0); - } - } - } - - fn validate_seccomp_filter( - rules: Vec<(i64, Vec)>, - validation_fn: fn(), - should_fail: bool, - ) { - let failure_code: i32 = 1000; - - let mut rule_map: SeccompRuleMap = rules.into_iter().collect(); - - for syscall in EXTRA_SYSCALLS.iter() { - rule_map - .entry(*syscall) - .or_default() - .append(&mut vec![SeccompRule::new(vec![], SeccompAction::Allow)]); - } - - // Build seccomp filter. - let filter = SeccompFilter::new( - rule_map, - SeccompAction::Errno(u32::try_from(failure_code).unwrap()), - ARCH, - ) - .unwrap(); - - // We need to run the validation inside another thread in order to avoid setting - // the seccomp filter for the entire unit tests process. - let errno = thread::spawn(move || { - // Install the filter. - install_filter(filter.try_into().unwrap()); - - // Call the validation fn. - validation_fn(); - - // Return errno. - std::io::Error::last_os_error().raw_os_error().unwrap() - }) - .join() - .unwrap(); - - // In case of a seccomp denial `errno` should be `failure_code` - if should_fail { - assert_eq!(errno, failure_code); - } else { - assert_ne!(errno, failure_code); - } - } - - #[test] - fn test_eq_operator() { - // check use cases for SeccompCmpArgLen::DWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(1, SeccompCmpArgLen::Dword, Eq, KVM_GET_PIT2).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, KVM_GET_PIT2 as IoctlRequest); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, 0); - }, - true, - ); - - // check use cases for SeccompCmpArgLen::QWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(2, SeccompCmpArgLen::Qword, Eq, u64::MAX).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, 0, u64::MAX); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, 0, 0); - }, - true, - ); - } - - #[test] - fn test_ge_operator() { - // check use case for SeccompCmpArgLen::DWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(1, SeccompCmpArgLen::Dword, Ge, KVM_GET_PIT2).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, KVM_GET_PIT2 as IoctlRequest); - libc::ioctl(0, (KVM_GET_PIT2 + 1) as IoctlRequest); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, (KVM_GET_PIT2 - 1) as IoctlRequest); - }, - true, - ); - - // check use case for SeccompCmpArgLen::QWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(2, SeccompCmpArgLen::Qword, Ge, u64::from(u32::MAX)).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, 0, u64::from(u32::MAX)); - libc::ioctl(0, 0, u64::from(u32::MAX) + 1); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, 0, 1); - }, - true, - ); - } - - #[test] - fn test_gt_operator() { - // check use case for SeccompCmpArgLen::DWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(1, SeccompCmpArgLen::Dword, Gt, KVM_GET_PIT2).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, (KVM_GET_PIT2 + 1) as IoctlRequest); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, KVM_GET_PIT2 as IoctlRequest); - }, - true, - ); - - // check use case for SeccompCmpArgLen::QWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(2, SeccompCmpArgLen::Qword, Gt, u64::from(u32::MAX) + 10).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, 0, u64::from(u32::MAX) + 11); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, 0, u64::from(u32::MAX) + 10); - }, - true, - ); - } - - #[test] - fn test_le_operator() { - // check use case for SeccompCmpArgLen::DWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(1, SeccompCmpArgLen::Dword, Le, KVM_GET_PIT2).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, KVM_GET_PIT2 as IoctlRequest); - libc::ioctl(0, (KVM_GET_PIT2 - 1) as IoctlRequest); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, (KVM_GET_PIT2 + 1) as IoctlRequest); - }, - true, - ); - - // check use case for SeccompCmpArgLen::QWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(2, SeccompCmpArgLen::Qword, Le, u64::from(u32::MAX) + 10).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, 0, u64::from(u32::MAX) + 10); - libc::ioctl(0, 0, u64::from(u32::MAX) + 9); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, 0, u64::from(u32::MAX) + 11); - }, - true, - ); - } - - #[test] - fn test_lt_operator() { - // check use case for SeccompCmpArgLen::DWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(1, SeccompCmpArgLen::Dword, Lt, KVM_GET_PIT2).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, (KVM_GET_PIT2 - 1) as IoctlRequest); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, KVM_GET_PIT2 as IoctlRequest); - }, - true, - ); - - // check use case for SeccompCmpArgLen::QWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(2, SeccompCmpArgLen::Qword, Lt, u64::from(u32::MAX) + 10).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, 0, u64::from(u32::MAX) + 9); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, 0, u64::from(u32::MAX) + 10); - }, - true, - ); - } - - #[test] - fn test_masked_eq_operator() { - // check use case for SeccompCmpArgLen::DWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new( - 1, - SeccompCmpArgLen::Dword, - MaskedEq(KVM_GET_PIT2_MSB), - KVM_GET_PIT2, - ) - .unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, KVM_GET_PIT2 as IoctlRequest); - libc::ioctl(0, KVM_GET_PIT2_MSB as IoctlRequest); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, KVM_GET_PIT2_LSB as IoctlRequest); - }, - true, - ); - - // check use case for SeccompCmpArgLen::QWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new( - 2, - SeccompCmpArgLen::Qword, - MaskedEq(u64::from(u32::MAX)), - u64::MAX, - ) - .unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, 0, u64::from(u32::MAX)); - libc::ioctl(0, 0, u64::MAX); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, 0, 0); - }, - true, - ); - } - - #[test] - fn test_ne_operator() { - // check use case for SeccompCmpArgLen::DWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(1, SeccompCmpArgLen::Dword, Ne, KVM_GET_PIT2).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, 0); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, KVM_GET_PIT2 as IoctlRequest); - }, - true, - ); - - // check use case for SeccompCmpArgLen::QWORD - let rules = vec![allow_syscall_if( - libc::SYS_ioctl, - vec![SeccompRule::new( - vec![Cond::new(2, SeccompCmpArgLen::Qword, Ne, u64::MAX).unwrap()], - SeccompAction::Allow, - )], - )]; - // check syscalls that are supposed to work - validate_seccomp_filter( - rules.clone(), - || unsafe { - libc::ioctl(0, 0, 0); - }, - false, - ); - // check syscalls that are not supposed to work - validate_seccomp_filter( - rules, - || unsafe { - libc::ioctl(0, 0, u64::MAX); - }, - true, - ); - } - - // Checks that rule gets translated correctly into BPF statements. - #[test] - fn test_rule_bpf_output() { - Cond::new(6, ArgLen::Qword, Eq, 1).unwrap_err(); - - // Builds rule. - let rule = SeccompRule::new( - vec![ - Cond::new(0, ArgLen::Dword, Eq, 1).unwrap(), - Cond::new(2, ArgLen::Qword, MaskedEq(0b1010), 14).unwrap(), - ], - SeccompAction::Allow, - ); - - let (msb_offset, lsb_offset) = { (4, 0) }; - - // Builds hardcoded BPF instructions. - let instructions = vec![ - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 10), - BPF_STMT(0x20, 32 + msb_offset), - BPF_STMT(0x54, 0), - BPF_JUMP(0x15, 0, 0, 6), - BPF_STMT(0x20, 32 + lsb_offset), - BPF_STMT(0x54, 0b1010), - BPF_JUMP(0x15, 14 & 0b1010, 0, 3), - BPF_STMT(0x20, 16 + lsb_offset), - BPF_JUMP(0x15, 1, 0, 1), - BPF_STMT(0x06, 0x7fff_0000), - ]; - - // Compares translated rule with hardcoded BPF instructions. - let bpfprog: BpfProgram = rule.into(); - assert_eq!(bpfprog, instructions); - } - - // Checks that rule with too many conditions gets translated correctly into BPF statements - // using three helper jumps. - #[test] - fn test_rule_many_conditions_bpf_output() { - // Builds rule. - let mut conditions = Vec::with_capacity(43); - for _ in 0..42 { - conditions.push(Cond::new(0, ArgLen::Qword, MaskedEq(0), 0).unwrap()); - } - conditions.push(Cond::new(0, ArgLen::Qword, Eq, 0).unwrap()); - let rule = SeccompRule::new(conditions, SeccompAction::Allow); - - let (msb_offset, lsb_offset) = { (4, 0) }; - - // Builds hardcoded BPF instructions. - let mut instructions = vec![ - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 6), - BPF_STMT(0x20, 16 + msb_offset), - BPF_JUMP(0x15, 0, 0, 3), - BPF_STMT(0x20, 16 + lsb_offset), - BPF_JUMP(0x15, 0, 0, 1), - BPF_STMT(0x05, 2), - BPF_STMT(0x05, 254), - BPF_STMT(0x05, 254), - ]; - let mut offset = 253; - for _ in 0..42 { - offset -= 6; - instructions.append(&mut vec![ - BPF_STMT(0x20, 16 + msb_offset), - BPF_STMT(0x54, 0), - BPF_JUMP(0x15, 0, 0, offset + 3), - BPF_STMT(0x20, 16 + lsb_offset), - BPF_STMT(0x54, 0), - BPF_JUMP(0x15, 0, 0, offset), - ]); - } - instructions.push(BPF_STMT(0x06, 0x7fff_0000)); - - // Compares translated rule with hardcoded BPF instructions. - let bpfprog: BpfProgram = rule.into(); - assert_eq!(bpfprog, instructions); - } - - fn create_test_bpf_filter(arg_len: ArgLen) -> SeccompFilter { - SeccompFilter::new( - vec![ - allow_syscall_if( - 1, - vec![ - SeccompRule::new( - vec![ - Cond::new(2, arg_len.clone(), Le, 14).unwrap(), - Cond::new(2, arg_len.clone(), Ne, 10).unwrap(), - ], - SeccompAction::Allow, - ), - SeccompRule::new( - vec![ - Cond::new(2, arg_len.clone(), Gt, 20).unwrap(), - Cond::new(2, arg_len.clone(), Lt, 30).unwrap(), - ], - SeccompAction::Allow, - ), - SeccompRule::new( - vec![Cond::new(2, arg_len.clone(), Ge, 42).unwrap()], - SeccompAction::Allow, - ), - ], - ), - allow_syscall_if( - 9, - vec![SeccompRule::new( - vec![Cond::new(1, arg_len, MaskedEq(0b100), 36).unwrap()], - SeccompAction::Allow, - )], - ), - ] - .into_iter() - .collect(), - SeccompAction::Trap, - ARCH, - ) - .unwrap() - } - - #[test] - fn test_filter_bpf_output_dword() { - // Compares translated filter with hardcoded BPF program. - { - let mut empty_rule_map = BTreeMap::new(); - empty_rule_map.insert(1, vec![]); - SeccompFilter::new(empty_rule_map, SeccompAction::Allow, ARCH).unwrap_err(); - } - - let filter = create_test_bpf_filter(ArgLen::Dword); - - let mut instructions = Vec::new(); - instructions.extend(VALIDATE_ARCHITECTURE(ARCH.try_into().unwrap())); - instructions.extend(vec![ - BPF_STMT(0x20, 0), - BPF_JUMP(0x15, 1, 0, 1), - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 6), - BPF_STMT(0x20, 32), - BPF_JUMP(0x15, 10, 3, 0), - BPF_STMT(0x20, 32), - BPF_JUMP(0x25, 14, 1, 0), - BPF_STMT(0x06, 0x7fff_0000), - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 6), - BPF_STMT(0x20, 32), - BPF_JUMP(0x35, 30, 3, 0), - BPF_STMT(0x20, 32), - BPF_JUMP(0x25, 20, 0, 1), - BPF_STMT(0x06, 0x7fff_0000), - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 4), - BPF_STMT(0x20, 32), - BPF_JUMP(0x35, 42, 0, 1), - BPF_STMT(0x06, 0x7fff_0000), - BPF_STMT(0x06, 0x0003_0000), - BPF_JUMP(0x15, 9, 0, 1), - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 5), - BPF_STMT(0x20, 24), - BPF_STMT(0x54, 0b100), - BPF_JUMP(0x15, 36 & 0b100, 0, 1), - BPF_STMT(0x06, 0x7fff_0000), - BPF_STMT(0x06, 0x0003_0000), - BPF_STMT(0x06, 0x0003_0000), - ]); - - let bpfprog: BpfProgram = filter.try_into().unwrap(); - assert_eq!(bpfprog, instructions); - } - - #[test] - fn test_filter_bpf_output_qword() { - // Compares translated filter with hardcoded BPF program. - { - let mut empty_rule_map = BTreeMap::new(); - empty_rule_map.insert(1, vec![]); - SeccompFilter::new(empty_rule_map, SeccompAction::Allow, ARCH).unwrap_err(); - } - - let filter = create_test_bpf_filter(ArgLen::Qword); - - let mut instructions = Vec::new(); - instructions.extend(VALIDATE_ARCHITECTURE(ARCH.try_into().unwrap())); - instructions.extend(vec![ - BPF_STMT(0x20, 0), - BPF_JUMP(0x15, 1, 0, 1), - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 11), - BPF_STMT(0x20, 36), - BPF_JUMP(0x15, 0, 0, 2), - BPF_STMT(0x20, 32), - BPF_JUMP(0x15, 10, 6, 0), - BPF_STMT(0x20, 36), - BPF_JUMP(0x25, 0, 4, 0), - BPF_JUMP(0x15, 0, 0, 2), - BPF_STMT(0x20, 32), - BPF_JUMP(0x25, 14, 1, 0), - BPF_STMT(0x06, 0x7fff_0000), - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 12), - BPF_STMT(0x20, 36), - BPF_JUMP(0x25, 0, 9, 0), - BPF_JUMP(0x15, 0, 0, 2), - BPF_STMT(0x20, 32), - BPF_JUMP(0x35, 30, 6, 0), - BPF_STMT(0x20, 36), - BPF_JUMP(0x25, 0, 3, 0), - BPF_JUMP(0x15, 0, 0, 3), - BPF_STMT(0x20, 32), - BPF_JUMP(0x25, 20, 0, 1), - BPF_STMT(0x06, 0x7fff_0000), - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 7), - BPF_STMT(0x20, 36), - BPF_JUMP(0x25, 0, 3, 0), - BPF_JUMP(0x15, 0, 0, 3), - BPF_STMT(0x20, 32), - BPF_JUMP(0x35, 42, 0, 1), - BPF_STMT(0x06, 0x7fff_0000), - BPF_STMT(0x06, 0x0003_0000), - BPF_JUMP(0x15, 9, 0, 1), - BPF_STMT(0x05, 1), - BPF_STMT(0x05, 8), - BPF_STMT(0x20, 28), - BPF_STMT(0x54, 0), - BPF_JUMP(0x15, 0, 0, 4), - BPF_STMT(0x20, 24), - BPF_STMT(0x54, 0b100), - BPF_JUMP(0x15, 36 & 0b100, 0, 1), - BPF_STMT(0x06, 0x7fff_0000), - BPF_STMT(0x06, 0x0003_0000), - BPF_STMT(0x06, 0x0003_0000), - ]); - - let bpfprog: BpfProgram = filter.try_into().unwrap(); - assert_eq!(bpfprog, instructions); - } - - #[test] - fn test_bpf_expanding_functions() { - // Compares the output of the BPF instruction generating functions to hardcoded - // instructions. - assert_eq!( - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16), - sock_filter { - code: 0x20, - jt: 0, - jf: 0, - k: 16, - } - ); - assert_eq!( - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 10, 2, 5), - sock_filter { - code: 0x15, - jt: 2, - jf: 5, - k: 10, - } - ); - } - - #[test] - fn test_bpf_functions() { - { - let ret = VALIDATE_ARCHITECTURE(ARCH.try_into().unwrap()); - let instructions = vec![ - sock_filter { - code: 32, - jt: 0, - jf: 0, - k: 4, - }, - sock_filter { - code: 21, - jt: 1, - jf: 0, - #[cfg(target_arch = "x86_64")] - k: AUDIT_ARCH_X86_64, - #[cfg(target_arch = "aarch64")] - k: AUDIT_ARCH_AARCH64, - }, - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: SECCOMP_RET_KILL_PROCESS, - }, - ]; - assert_eq!(ret, instructions); - } - - { - let ret = EXAMINE_SYSCALL(); - let instructions = vec![sock_filter { - code: 32, - jt: 0, - jf: 0, - k: 0, - }]; - assert_eq!(ret, instructions); - } - } - - #[test] - fn test_empty_filter() { - // An empty filter should always return the default action. - // For example, for an empty allowlist, it should always trap/kill, - // for an empty denylist, it should allow allow all system calls. - - let mut expected_program = Vec::new(); - expected_program.extend(VALIDATE_ARCHITECTURE(ARCH.try_into().unwrap())); - expected_program.extend(vec![BPF_STMT(0x06, 0x7fff_0000)]); - - let empty_rule_map = BTreeMap::new(); - let filter = SeccompFilter::new(empty_rule_map, SeccompAction::Allow, ARCH).unwrap(); - let prog: BpfProgram = filter.try_into().unwrap(); - - assert_eq!(expected_program, prog); - - // This should allow any system calls. - let pid = thread::spawn(move || { - // Install the filter. - install_filter(prog); - - unsafe { libc::getpid() } - }) - .join() - .unwrap(); - - // Check that the getpid syscall returned successfully. - assert!(pid > 0); - } - - #[test] - fn test_error_messages() { - assert_eq!( - format!("{}", FilterError::EmptyRulesVector), - "The seccomp rules vector is empty." - ); - assert_eq!( - format!("{}", FilterError::FilterTooLarge), - "The seccomp filter contains too many BPF instructions." - ); - assert_eq!( - format!("{}", FilterError::InvalidArgumentNumber), - "The seccomp rule contains an invalid argument number." - ); - assert_eq!( - format!( - "{}", - FilterError::Arch(TargetArchError::InvalidString("lala".to_string())) - ), - format!("{0}", TargetArchError::InvalidString("lala".to_string())) - ); - } - - #[test] - fn test_from_seccomp_action() { - assert_eq!(0x7fff_0000, u32::from(SeccompAction::Allow)); - assert_eq!(0x0005_002a, u32::from(SeccompAction::Errno(42))); - assert_eq!(0x0000_0000, u32::from(SeccompAction::KillThread)); - assert_eq!(0x8000_0000, u32::from(SeccompAction::KillProcess)); - assert_eq!(0x7ffc_0000, u32::from(SeccompAction::Log)); - assert_eq!(0x7ff0_002a, u32::from(SeccompAction::Trace(42))); - assert_eq!(0x0003_0000, u32::from(SeccompAction::Trap)); - } - - #[test] - fn test_validate_condition() { - // Invalid argument number - assert_eq!( - Cond::new(90, ArgLen::Dword, Eq, 65), - Err(FilterError::InvalidArgumentNumber) - ); - - // Valid argument number - Cond::new(0, ArgLen::Dword, Eq, 65).unwrap(); - } - - #[test] - fn test_seccomp_filter_validate() { - // Failure cases. - { - // Syscall has no rules. - assert_eq!( - SeccompFilter::new( - vec![(1, vec![]),].into_iter().collect(), - SeccompAction::Trap, - ARCH, - ) - .unwrap_err(), - FilterError::EmptyRulesVector - ); - // Syscall has multiple empty rules. - assert_eq!( - SeccompFilter::new( - vec![( - 1, - vec![ - SeccompRule::new(vec![], SeccompAction::Allow), - SeccompRule::new(vec![], SeccompAction::Allow) - ] - ),] - .into_iter() - .collect(), - SeccompAction::Trap, - ARCH, - ) - .unwrap_err(), - FilterError::ConflictingRules(1) - ); - - // Syscall has both empty rules condition-based rules. - assert_eq!( - SeccompFilter::new( - vec![( - 1, - vec![ - SeccompRule::new(vec![], SeccompAction::Allow), - SeccompRule::new( - vec![ - Cond::new(2, ArgLen::Dword, Le, 14).unwrap(), - Cond::new(1, ArgLen::Dword, Ne, 10).unwrap(), - ], - SeccompAction::Allow, - ), - ] - ),] - .into_iter() - .collect(), - SeccompAction::Trap, - ARCH, - ) - .unwrap_err(), - FilterError::ConflictingRules(1) - ); - } - } -} diff --git a/src/seccompiler/src/bin.rs b/src/seccompiler/src/bin.rs new file mode 100644 index 00000000000..749d04b570d --- /dev/null +++ b/src/seccompiler/src/bin.rs @@ -0,0 +1,40 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use clap::Parser; +use seccompiler::{CompilationError, compile_bpf}; + +const DEFAULT_OUTPUT_FILENAME: &str = "seccomp_binary_filter.out"; + +#[derive(Debug, Parser)] +#[command(version = format!("v{}", env!("CARGO_PKG_VERSION")))] +struct Cli { + #[arg( + short, + long, + help = "The computer architecture where the BPF program runs. Supported architectures: \ + x86_64, aarch64." + )] + target_arch: String, + #[arg(short, long, help = "File path of the JSON input.")] + input_file: String, + #[arg(short, long, help = "Optional path of the output file.", default_value = DEFAULT_OUTPUT_FILENAME)] + output_file: String, + #[arg( + short, + long, + help = "Deprecated! Transforms the filters into basic filters. Drops all argument checks \ + and rule-level actions. Not recommended." + )] + basic: bool, +} + +fn main() -> Result<(), CompilationError> { + let cli = Cli::parse(); + compile_bpf( + &cli.input_file, + &cli.target_arch, + &cli.output_file, + cli.basic, + ) +} diff --git a/src/seccompiler/src/bindings.rs b/src/seccompiler/src/bindings.rs new file mode 100644 index 00000000000..969ea91cd1c --- /dev/null +++ b/src/seccompiler/src/bindings.rs @@ -0,0 +1,171 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2021 Sony Group Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +//! Raw FFI bindings for libseccomp library + +use std::os::raw::*; + +pub const MINUS_EEXIST: i32 = -libc::EEXIST; + +/// Filter context/handle (`*mut`) +pub type scmp_filter_ctx = *mut c_void; +/// Filter context/handle (`*const`) +pub type const_scmp_filter_ctx = *const c_void; + +/// Comparison operators +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(C)] +pub enum scmp_compare { + _SCMP_CMP_MIN = 0, + /// not equal + SCMP_CMP_NE = 1, + /// less than + SCMP_CMP_LT = 2, + /// less than or equal + SCMP_CMP_LE = 3, + /// equal + SCMP_CMP_EQ = 4, + /// greater than or equal + SCMP_CMP_GE = 5, + /// greater than + SCMP_CMP_GT = 6, + /// masked equality + SCMP_CMP_MASKED_EQ = 7, + _SCMP_CMP_MAX, +} + +/// Argument datum +pub type scmp_datum_t = u64; + +/// Argument / Value comparison definition +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(C)] +pub struct scmp_arg_cmp { + /// argument number, starting at 0 + pub arg: c_uint, + /// the comparison op, e.g. `SCMP_CMP_*` + pub op: scmp_compare, + pub datum_a: scmp_datum_t, + pub datum_b: scmp_datum_t, +} + +pub const SCMP_ARCH_X86_64: u32 = 0xc000003e; +pub const SCMP_ARCH_AARCH64: u32 = 0xc00000b7; +/// Kill the process +pub const SCMP_ACT_KILL_PROCESS: u32 = 0x80000000; +/// Kill the thread +pub const SCMP_ACT_KILL_THREAD: u32 = 0x00000000; +/// Throw a `SIGSYS` signal +pub const SCMP_ACT_TRAP: u32 = 0x00030000; +/// Notifies userspace +pub const SCMP_ACT_ERRNO_MASK: u32 = 0x00050000; +/// Return the specified error code +#[must_use] +pub const fn SCMP_ACT_ERRNO(x: u16) -> u32 { + SCMP_ACT_ERRNO_MASK | x as u32 +} +pub const SCMP_ACT_TRACE_MASK: u32 = 0x7ff00000; +/// Notify a tracing process with the specified value +#[must_use] +pub const fn SCMP_ACT_TRACE(x: u16) -> u32 { + SCMP_ACT_TRACE_MASK | x as u32 +} +/// Allow the syscall to be executed after the action has been logged +pub const SCMP_ACT_LOG: u32 = 0x7ffc0000; +/// Allow the syscall to be executed +pub const SCMP_ACT_ALLOW: u32 = 0x7fff0000; + +#[link(name = "seccomp")] +unsafe extern "C" { + /// Initialize the filter state + /// + /// - `def_action`: the default filter action + /// + /// This function initializes the internal seccomp filter state and should + /// be called before any other functions in this library to ensure the filter + /// state is initialized. Returns a filter context on success, `ptr::null()` on failure. + pub safe fn seccomp_init(def_action: u32) -> scmp_filter_ctx; + + /// Adds an architecture to the filter + /// + /// - `ctx`: the filter context + /// - `arch_token`: the architecture token, e.g. `SCMP_ARCH_*` + /// + /// This function adds a new architecture to the given seccomp filter context. + /// Any new rules added after this function successfully returns will be added + /// to this architecture but existing rules will not be added to this + /// architecture. If the architecture token is [`SCMP_ARCH_NATIVE`] then the native + /// architecture will be assumed. Returns zero on success, `-libc::EEXIST` if + /// specified architecture is already present, other negative values on failure. + pub fn seccomp_arch_add(ctx: scmp_filter_ctx, arch_token: u32) -> c_int; + + /// Resolve a syscall name to a number + /// + /// - `name`: the syscall name + /// + /// Resolve the given syscall name to the syscall number. Returns the syscall + /// number on success, including negative pseudo syscall numbers (e.g. `__PNR_*`); + /// returns [`__NR_SCMP_ERROR`] on failure. + pub fn seccomp_syscall_resolve_name(name: *const c_char) -> c_int; + + /// Add a new rule to the filter + /// + /// - `ctx`: the filter context + /// - `action`: the filter action + /// - `syscall`: the syscall number + /// - `arg_cnt`: the number of argument filters in the argument filter chain + /// - `...`: [`scmp_arg_cmp`] structs + /// + /// This function adds a series of new argument/value checks to the seccomp + /// filter for the given syscall; multiple argument/value checks can be + /// specified and they will be chained together (AND'd together) in the filter. + /// If the specified rule needs to be adjusted due to architecture specifics it + /// will be adjusted without notification. Returns zero on success, negative + /// values on failure. + pub fn seccomp_rule_add( + ctx: scmp_filter_ctx, + action: u32, + syscall: c_int, + arg_cnt: c_uint, + ... + ) -> c_int; + + /// Add a new rule to the filter + /// + /// - `ctx`: the filter context + /// - `action`: the filter action + /// - `syscall`: the syscall number + /// - `arg_cnt`: the number of elements in the arg_array parameter + /// - `arg_array`: array of [`scmp_arg_cmp`] structs + /// + /// This function adds a series of new argument/value checks to the seccomp + /// filter for the given syscall; multiple argument/value checks can be + /// specified and they will be chained together (AND'd together) in the filter. + /// If the specified rule needs to be adjusted due to architecture specifics it + /// will be adjusted without notification. Returns zero on success, negative + /// values on failure. + pub fn seccomp_rule_add_array( + ctx: scmp_filter_ctx, + action: u32, + syscall: c_int, + arg_cnt: c_uint, + arg_array: *const scmp_arg_cmp, + ) -> c_int; + + /// Generate seccomp Berkeley Packet Filter (BPF) code and export it to a file + /// + /// - `ctx`: the filter context + /// - `fd`: the destination fd + /// + /// This function generates seccomp Berkeley Packer Filter (BPF) code and writes + /// it to the given fd. Returns zero on success, negative values on failure. + pub fn seccomp_export_bpf(ctx: const_scmp_filter_ctx, fd: c_int) -> c_int; +} + +/// Negative pseudo syscall number returned by some functions in case of an error +pub const __NR_SCMP_ERROR: c_int = -1; diff --git a/src/seccompiler/src/common.rs b/src/seccompiler/src/common.rs deleted file mode 100644 index 80ff96ad9f3..00000000000 --- a/src/seccompiler/src/common.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -//! Module that defines common data structures used by both the library crate -//! and seccompiler-bin. - -use serde::{Deserialize, Serialize}; - -/// The maximum seccomp-BPF program length allowed by the linux kernel. -pub const BPF_MAX_LEN: u16 = 4096; - -/// BPF instruction structure definition. -/// See /usr/include/linux/filter.h . -#[repr(C)] -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -#[doc(hidden)] -pub struct sock_filter { - pub code: ::std::os::raw::c_ushort, - pub jt: ::std::os::raw::c_uchar, - pub jf: ::std::os::raw::c_uchar, - pub k: ::std::os::raw::c_uint, -} - -/// Program made up of a sequence of BPF instructions. -pub type BpfProgram = Vec; diff --git a/src/seccompiler/src/compiler.rs b/src/seccompiler/src/compiler.rs deleted file mode 100644 index 73ff9f706ca..00000000000 --- a/src/seccompiler/src/compiler.rs +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -//! Module defining the logic for compiling the deserialized filter objects into the IR. -//! Used by seccompiler-bin. -//! -//! Via the `Compiler::compile_blob()` method, it also drives the entire JSON -> BLOB -//! transformation process. -//! -//! It also defines some of the objects that a JSON seccomp filter is deserialized into: -//! [`Filter`](struct.Filter.html), -//! [`SyscallRule`](struct.SyscallRule.html). -// -//! The rest of objects are deserialized directly into the IR (intermediate representation): -//! [`SeccompCondition`](../backend/struct.SeccompCondition.html), -//! [`SeccompAction`](../backend/enum.SeccompAction.html), -//! [`SeccompCmpOp`](../backend/enum.SeccompCmpOp.html), -//! [`SeccompCmpArgLen`](../backend/enum.SeccompCmpArgLen.html). - -use std::collections::BTreeMap; -use std::convert::{Into, TryInto}; -use std::{fmt, result}; - -use serde::de::{self, Error as _, MapAccess, Visitor}; -use serde::Deserialize; - -use crate::backend::{ - Comment, FilterError, SeccompAction, SeccompCondition, SeccompFilter, SeccompRule, - SeccompRuleMap, TargetArch, -}; -use crate::common::BpfProgram; -use crate::syscall_table::SyscallTable; - -/// Errors compiling Filters into BPF. -#[derive(Debug, PartialEq, thiserror::Error, displaydoc::Display)] -pub enum CompilationError { - /// `filter_action` and `default_action` are equal. - IdenticalActions, - /// {0} - Filter(#[from] FilterError), - /// Invalid syscall name: {0} for given arch: {1:?}. - SyscallName(String, TargetArch), -} - -/// Deserializable object that represents the Json filter file. -#[derive(Debug)] -pub struct JsonFile(pub BTreeMap); - -// Implement a custom deserializer, that returns an error for duplicate thread keys. -impl<'de> Deserialize<'de> for JsonFile { - fn deserialize(deserializer: D) -> result::Result - where - D: de::Deserializer<'de>, - { - #[derive(Debug)] - struct JsonFileVisitor; - - impl<'d> Visitor<'d> for JsonFileVisitor { - type Value = BTreeMap; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> { - f.write_str("a map of filters") - } - - fn visit_map(self, mut access: M) -> result::Result - where - M: MapAccess<'d>, - { - let mut values = Self::Value::new(); - - while let Some((key, value)) = access.next_entry()? { - if values.insert(key, value).is_some() { - return Err(M::Error::custom("duplicate filter key")); - }; - } - - Ok(values) - } - } - Ok(JsonFile(deserializer.deserialize_map(JsonFileVisitor)?)) - } -} - -/// Deserializable object representing a syscall rule. -#[derive(Debug, Deserialize, PartialEq, Clone)] -#[serde(deny_unknown_fields)] -pub struct SyscallRule { - /// Name of the syscall. - syscall: String, - /// Rule conditions. - #[serde(rename = "args")] - conditions: Option>, - /// Optional empty value, represents a `comment` property in the JSON file. - comment: Option, -} - -impl SyscallRule { - /// Perform semantic checks after deserialization. - fn validate(&self) -> Result<(), CompilationError> { - // Validate all `SeccompCondition`s. - if let Some(conditions) = self.conditions.as_ref() { - return conditions - .iter() - .filter_map(|cond| cond.validate().err()) - .next() - .map_or(Ok(()), |err| Err(CompilationError::Filter(err))); - } - - Ok(()) - } -} - -/// Deserializable seccomp filter. Refers to one thread category. -#[derive(Deserialize, PartialEq, Debug, Clone)] -#[serde(deny_unknown_fields)] -pub struct Filter { - /// Default action if no rules match. e.g. `Kill` for an AllowList. - default_action: SeccompAction, - /// Default action if a rule matches. e.g. `Allow` for an AllowList. - filter_action: SeccompAction, - /// The collection of `SyscallRule`s. - filter: Vec, -} - -impl Filter { - /// Perform semantic checks after deserialization. - fn validate(&self) -> Result<(), CompilationError> { - // Doesn't make sense to have equal default and on-match actions. - if self.default_action == self.filter_action { - return Err(CompilationError::IdenticalActions); - } - - // Validate all `SyscallRule`s. - self.filter - .iter() - .filter_map(|syscall_rule| syscall_rule.validate().err()) - .next() - .map_or(Ok(()), Err) - } -} - -/// Object responsible for compiling [`Filter`](struct.Filter.html)s into -/// [`BpfProgram`](../common/type.BpfProgram.html)s. -/// Uses the [`SeccompFilter`](../backend/struct.SeccompFilter.html) interface as an IR language. -#[derive(Debug)] -pub struct Compiler { - /// Target architecture. Can be different from the current `target_arch`. - arch: TargetArch, - /// Target-specific syscall table. - syscall_table: SyscallTable, -} - -impl Compiler { - /// Create a new `Compiler` instance, for the given target architecture. - pub fn new(arch: TargetArch) -> Self { - Self { - arch, - syscall_table: SyscallTable::new(arch), - } - } - - /// Perform semantic checks after deserialization. - fn validate_filters(&self, filters: &BTreeMap) -> Result<(), CompilationError> { - // Validate all `Filter`s. - filters - .iter() - .filter_map(|(_, filter)| filter.validate().err()) - .next() - .map_or(Ok(()), Err) - } - - /// Main compilation function. - pub fn compile_blob( - &self, - filters: BTreeMap, - is_basic: bool, - ) -> Result, CompilationError> { - self.validate_filters(&filters)?; - let mut bpf_map: BTreeMap = BTreeMap::new(); - - for (thread_name, filter) in filters.into_iter() { - if is_basic { - bpf_map.insert( - thread_name, - self.make_basic_seccomp_filter(filter)?.try_into()?, - ); - } else { - bpf_map.insert(thread_name, self.make_seccomp_filter(filter)?.try_into()?); - } - } - Ok(bpf_map) - } - - /// Transforms the deserialized `Filter` into a `SeccompFilter` (IR language). - fn make_seccomp_filter(&self, filter: Filter) -> Result { - let mut rule_map: SeccompRuleMap = SeccompRuleMap::new(); - let filter_action = &filter.filter_action; - - for syscall_rule in filter.filter { - let syscall_name = syscall_rule.syscall; - let action = filter_action.clone(); - let syscall_nr = self - .syscall_table - .get_syscall_nr(&syscall_name) - .ok_or_else(|| CompilationError::SyscallName(syscall_name.clone(), self.arch))?; - let rule_accumulator = rule_map.entry(syscall_nr).or_default(); - - match syscall_rule.conditions { - Some(conditions) => rule_accumulator.push(SeccompRule::new(conditions, action)), - None => rule_accumulator.push(SeccompRule::new(vec![], action)), - }; - } - - SeccompFilter::new(rule_map, filter.default_action, self.arch.into()) - .map_err(CompilationError::Filter) - } - - /// Transforms the deserialized `Filter` into a basic `SeccompFilter` (IR language). - /// This filter will drop any argument checks and any rule-level action. - /// All rules will trigger the filter-level `filter_action`. - fn make_basic_seccomp_filter(&self, filter: Filter) -> Result { - let mut rule_map: SeccompRuleMap = SeccompRuleMap::new(); - let filter_action = &filter.filter_action; - - for syscall_rule in filter.filter { - let syscall_name = syscall_rule.syscall; - // Basic filters bypass the rule-level action and use the filter_action. - let action = filter_action.clone(); - let syscall_nr = self - .syscall_table - .get_syscall_nr(&syscall_name) - .ok_or_else(|| CompilationError::SyscallName(syscall_name.clone(), self.arch))?; - - // If there is already an entry for this syscall, do nothing. - // Otherwise, insert an empty rule that triggers the filter_action. - rule_map - .entry(syscall_nr) - .or_insert_with(|| vec![SeccompRule::new(vec![], action)]); - } - - SeccompFilter::new(rule_map, filter.default_action, self.arch.into()) - .map_err(CompilationError::Filter) - } -} - -#[cfg(test)] -mod tests { - use std::collections::BTreeMap; - use std::convert::TryInto; - use std::env::consts::ARCH; - - use super::{CompilationError, Compiler, Filter, SyscallRule}; - use crate::backend::SeccompCmpArgLen::*; - use crate::backend::SeccompCmpOp::*; - use crate::backend::{ - FilterError, SeccompAction, SeccompCondition as Cond, SeccompFilter, SeccompRule, - TargetArch, - }; - - impl Filter { - pub fn new( - default_action: SeccompAction, - filter_action: SeccompAction, - filter: Vec, - ) -> Filter { - Filter { - default_action, - filter_action, - filter, - } - } - } - - impl SyscallRule { - pub fn new(syscall: String, conditions: Option>) -> SyscallRule { - SyscallRule { - syscall, - conditions, - comment: None, - } - } - } - - fn match_syscall(syscall_number: i64, action: SeccompAction) -> (i64, Vec) { - (syscall_number, vec![SeccompRule::new(vec![], action)]) - } - - fn match_syscall_if(syscall_number: i64, rules: Vec) -> (i64, Vec) { - (syscall_number, rules) - } - - #[test] - // Test the transformation of Filter objects into SeccompFilter objects. - // We test this private method because we are interested in seeing that the - // Filter -> SeccompFilter transformation is done correctly. - fn test_make_seccomp_filter() { - let compiler = Compiler::new(ARCH.try_into().unwrap()); - // Test a well-formed filter. Malformed filters are tested in test_compile_blob(). - let filter = Filter::new( - SeccompAction::Trap, - SeccompAction::Allow, - vec![ - SyscallRule::new("read".to_string(), None), - SyscallRule::new( - "futex".to_string(), - Some(vec![ - Cond::new(2, Dword, Le, 65).unwrap(), - Cond::new(1, Qword, Ne, 80).unwrap(), - ]), - ), - SyscallRule::new( - "futex".to_string(), - Some(vec![ - Cond::new(3, Qword, Gt, 65).unwrap(), - Cond::new(1, Qword, Lt, 80).unwrap(), - ]), - ), - SyscallRule::new( - "futex".to_string(), - Some(vec![Cond::new(3, Qword, Ge, 65).unwrap()]), - ), - SyscallRule::new( - "ioctl".to_string(), - Some(vec![Cond::new(3, Dword, MaskedEq(100), 65).unwrap()]), - ), - ], - ); - - // The expected IR. - let seccomp_filter = SeccompFilter::new( - vec![ - match_syscall( - compiler.syscall_table.get_syscall_nr("read").unwrap(), - SeccompAction::Allow, - ), - match_syscall_if( - compiler.syscall_table.get_syscall_nr("futex").unwrap(), - vec![ - SeccompRule::new( - vec![ - Cond::new(2, Dword, Le, 65).unwrap(), - Cond::new(1, Qword, Ne, 80).unwrap(), - ], - SeccompAction::Allow, - ), - SeccompRule::new( - vec![ - Cond::new(3, Qword, Gt, 65).unwrap(), - Cond::new(1, Qword, Lt, 80).unwrap(), - ], - SeccompAction::Allow, - ), - SeccompRule::new( - vec![Cond::new(3, Qword, Ge, 65).unwrap()], - SeccompAction::Allow, - ), - ], - ), - match_syscall_if( - compiler.syscall_table.get_syscall_nr("ioctl").unwrap(), - vec![SeccompRule::new( - vec![Cond::new(3, Dword, MaskedEq(100), 65).unwrap()], - SeccompAction::Allow, - )], - ), - ] - .into_iter() - .collect(), - SeccompAction::Trap, - ARCH, - ) - .unwrap(); - - assert_eq!( - compiler.make_seccomp_filter(filter).unwrap(), - seccomp_filter - ); - } - - #[test] - // Test the transformation of Filter objects into SeccompFilter objects. - // This `basic` alternative version of the make_seccomp_filter method drops argument checks. - fn test_make_basic_seccomp_filter() { - let compiler = Compiler::new(ARCH.try_into().unwrap()); - // Test a well-formed filter. Malformed filters are tested in test_compile_blob(). - let filter = Filter::new( - SeccompAction::Trap, - SeccompAction::Allow, - vec![ - SyscallRule::new("read".to_string(), None), - SyscallRule::new( - "futex".to_string(), - Some(vec![ - Cond::new(2, Dword, Le, 65).unwrap(), - Cond::new(1, Qword, Ne, 80).unwrap(), - ]), - ), - SyscallRule::new( - "futex".to_string(), - Some(vec![ - Cond::new(3, Qword, Gt, 65).unwrap(), - Cond::new(1, Qword, Lt, 80).unwrap(), - ]), - ), - SyscallRule::new( - "futex".to_string(), - Some(vec![Cond::new(3, Qword, Ge, 65).unwrap()]), - ), - SyscallRule::new( - "ioctl".to_string(), - Some(vec![Cond::new(3, Dword, MaskedEq(100), 65).unwrap()]), - ), - ], - ); - - // The expected IR. - let seccomp_filter = SeccompFilter::new( - vec![ - match_syscall( - compiler.syscall_table.get_syscall_nr("read").unwrap(), - SeccompAction::Allow, - ), - match_syscall( - compiler.syscall_table.get_syscall_nr("futex").unwrap(), - SeccompAction::Allow, - ), - match_syscall( - compiler.syscall_table.get_syscall_nr("ioctl").unwrap(), - SeccompAction::Allow, - ), - ] - .into_iter() - .collect(), - SeccompAction::Trap, - ARCH, - ) - .unwrap(); - - assert_eq!( - compiler.make_basic_seccomp_filter(filter).unwrap(), - seccomp_filter - ); - } - - #[test] - fn test_compile_blob() { - let compiler = Compiler::new(ARCH.try_into().unwrap()); - // Test with malformed filters. - - let mut wrong_syscall_name_filters = BTreeMap::new(); - wrong_syscall_name_filters.insert( - "T1".to_string(), - Filter::new( - SeccompAction::Trap, - SeccompAction::Allow, - vec![SyscallRule::new("wrong_syscall".to_string(), None)], - ), - ); - - assert_eq!( - compiler.compile_blob(wrong_syscall_name_filters, false), - Err(CompilationError::SyscallName( - "wrong_syscall".to_string(), - compiler.arch - )) - ); - - let mut identical_action_filters = BTreeMap::new(); - identical_action_filters.insert( - "T1".to_string(), - Filter::new(SeccompAction::Allow, SeccompAction::Allow, vec![]), - ); - - assert_eq!( - compiler.compile_blob(identical_action_filters, false), - Err(CompilationError::IdenticalActions) - ); - - // Test with correct filters. - let mut correct_filters = BTreeMap::new(); - correct_filters.insert( - "Thread1".to_string(), - Filter::new( - SeccompAction::Trap, - SeccompAction::Allow, - vec![ - SyscallRule::new("read".to_string(), None), - SyscallRule::new( - "futex".to_string(), - Some(vec![ - Cond::new(1, Dword, Eq, 65).unwrap(), - Cond::new(2, Qword, Le, 80).unwrap(), - ]), - ), - SyscallRule::new( - "futex".to_string(), - Some(vec![ - Cond::new(3, Dword, Eq, 65).unwrap(), - Cond::new(2, Qword, Le, 80).unwrap(), - ]), - ), - ], - ), - ); - - // We don't test the BPF compilation in this module. - // This is done in the seccomp/lib.rs module. - // Here, we only test the (Filter -> SeccompFilter) transformations. (High-level -> IR) - compiler - .compile_blob(correct_filters.clone(), false) - .unwrap(); - // Also test with basic filtering on. - compiler.compile_blob(correct_filters, true).unwrap(); - } - - #[test] - fn test_error_messages() { - assert_eq!( - format!("{}", CompilationError::IdenticalActions), - "`filter_action` and `default_action` are equal." - ); - assert_eq!( - format!( - "{}", - CompilationError::Filter(FilterError::InvalidArgumentNumber) - ), - "The seccomp rule contains an invalid argument number." - ); - assert_eq!( - format!( - "{}", - CompilationError::SyscallName("asdsad".to_string(), TargetArch::x86_64) - ), - format!( - "Invalid syscall name: {} for given arch: {}.", - "asdsad", "x86_64" - ) - ); - } -} diff --git a/src/seccompiler/src/lib.rs b/src/seccompiler/src/lib.rs index cc3e4756996..ecd157c4449 100644 --- a/src/seccompiler/src/lib.rs +++ b/src/seccompiler/src/lib.rs @@ -1,270 +1,196 @@ -// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -#![warn(missing_docs)] - -//! The library crate that defines common helper functions that are generally used in -//! conjunction with seccompiler-bin. - -pub mod backend; -pub mod common; -pub mod compiler; -/// Syscall tables -pub mod syscall_table; - use std::collections::HashMap; -use std::fmt::Debug; -use std::io::Read; -use std::sync::Arc; - -use bincode::{DefaultOptions, Error as BincodeError, Options}; -use common::BPF_MAX_LEN; -// Re-export the data types needed for calling the helper functions. -pub use common::{sock_filter, BpfProgram}; - -/// Type that associates a thread category to a BPF program. -pub type BpfThreadMap = HashMap>; - -// BPF structure definition for filter array. -// See /usr/include/linux/filter.h . -#[repr(C)] -struct sock_fprog { - pub len: ::std::os::raw::c_ushort, - pub filter: *const sock_filter, -} - -/// Reference to program made up of a sequence of BPF instructions. -pub type BpfProgramRef<'a> = &'a [sock_filter]; - -/// Binary filter deserialization errors. +use std::fs::File; +use std::io::{Read, Seek}; +use std::os::fd::{AsRawFd, FromRawFd}; +use std::os::unix::fs::MetadataExt; +use std::str::FromStr; + +use bincode::config; +use bincode::config::{Configuration, Fixint, Limit, LittleEndian}; +use bincode::error::EncodeError as BincodeError; + +mod bindings; +use bindings::*; + +pub mod types; +pub use types::*; +use zerocopy::IntoBytes; + +// This byte limit is passed to `bincode` to guard against a potential memory +// allocation DOS caused by binary filters that are too large. +// This limit can be safely determined since the maximum length of a BPF +// filter is 4096 instructions and Firecracker has a finite number of threads. +const DESERIALIZATION_BYTES_LIMIT: usize = 100_000; + +pub const BINCODE_CONFIG: Configuration> = + config::standard() + .with_fixed_int_encoding() + .with_limit::() + .with_little_endian(); + +/// Binary filter compilation errors. #[derive(Debug, thiserror::Error, displaydoc::Display)] -pub enum DeserializationError { - /// Bincode deserialization failed: {0} - Bincode(BincodeError), -} - -/// Filter installation errors. -#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] -pub enum InstallationError { - /// Filter length exceeds the maximum size of {BPF_MAX_LEN:} instructions - FilterTooLarge, - /// prctl` syscall failed with error code: {0} - Prctl(i32), -} - -/// Deserialize a BPF file into a collection of usable BPF filters. -/// Has an optional `bytes_limit` that is passed to bincode to constrain the maximum amount of -/// memory that we can allocate while performing the deserialization. -/// It's recommended that the integrator of the library uses this to prevent memory allocations -/// DOS-es. -pub fn deserialize_binary( - reader: R, - bytes_limit: Option, -) -> std::result::Result { - let result = match bytes_limit { - // Also add the default options. These are not part of the `DefaultOptions` as per - // this issue: https://github.com/servo/bincode/issues/333 - Some(limit) => DefaultOptions::new() - .with_fixint_encoding() - .allow_trailing_bytes() - .with_limit(limit) - .deserialize_from::>(reader), - // No limit is the default. - None => bincode::deserialize_from::>(reader), - }; - - Ok(result - .map_err(DeserializationError::Bincode)? - .into_iter() - .map(|(k, v)| (k.to_lowercase(), Arc::new(v))) - .collect()) +pub enum CompilationError { + /// Cannot open input file: {0} + IntputOpen(std::io::Error), + /// Cannot read input file: {0} + InputRead(std::io::Error), + /// Cannot deserialize json: {0} + JsonDeserialize(serde_json::Error), + /// Cannot parse arch: {0} + ArchParse(String), + /// Cannot create libseccomp context + LibSeccompContext, + /// Cannot add libseccomp arch + LibSeccompArch, + /// Cannot add libseccomp syscall + LibSeccompSycall, + /// Cannot add libseccomp syscall rule + LibSeccompRule, + /// Cannot export libseccomp bpf + LibSeccompExport, + /// Cannot create memfd: {0} + MemfdCreate(std::io::Error), + /// Cannot rewind memfd: {0} + MemfdRewind(std::io::Error), + /// Cannot read from memfd: {0} + MemfdRead(std::io::Error), + /// Cannot create output file: {0} + OutputCreate(std::io::Error), + /// Cannot serialize bfp: {0} + BincodeSerialize(BincodeError), } -/// Helper function for installing a BPF filter. -pub fn apply_filter(bpf_filter: BpfProgramRef) -> std::result::Result<(), InstallationError> { - // If the program is empty, don't install the filter. - if bpf_filter.is_empty() { - return Ok(()); - } +pub fn compile_bpf( + input_path: &str, + arch: &str, + out_path: &str, + basic: bool, +) -> Result<(), CompilationError> { + let mut file_content = String::new(); + File::open(input_path) + .map_err(CompilationError::IntputOpen)? + .read_to_string(&mut file_content) + .map_err(CompilationError::InputRead)?; + let bpf_map_json: BpfJson = + serde_json::from_str(&file_content).map_err(CompilationError::JsonDeserialize)?; + + let arch = TargetArch::from_str(arch).map_err(CompilationError::ArchParse)?; - // If the program length is greater than the limit allowed by the kernel, - // fail quickly. Otherwise, `prctl` will give a more cryptic error code. - let bpf_filter_len = - u16::try_from(bpf_filter.len()).map_err(|_| InstallationError::FilterTooLarge)?; - if bpf_filter_len > BPF_MAX_LEN { - return Err(InstallationError::FilterTooLarge); + // SAFETY: Safe because the parameters are valid. + let memfd_fd = unsafe { libc::memfd_create(c"bpf".as_ptr().cast(), 0) }; + if memfd_fd < 0 { + return Err(CompilationError::MemfdCreate( + std::io::Error::last_os_error(), + )); } // SAFETY: Safe because the parameters are valid. - unsafe { - { - let rc = libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); - if rc != 0 { - return Err(InstallationError::Prctl(*libc::__errno_location())); + let mut memfd = unsafe { File::from_raw_fd(memfd_fd) }; + + let mut bpf_map: HashMap> = HashMap::new(); + for (name, filter) in bpf_map_json.0.iter() { + let default_action = filter.default_action.to_scmp_type(); + let filter_action = filter.filter_action.to_scmp_type(); + + // SAFETY: Safe as all args are correct. + let bpf_filter = { + let r = seccomp_init(default_action); + if r.is_null() { + return Err(CompilationError::LibSeccompContext); } - } - - let bpf_prog = sock_fprog { - len: bpf_filter_len, - filter: bpf_filter.as_ptr(), + r }; - let bpf_prog_ptr = &bpf_prog as *const sock_fprog; - { - let rc = libc::prctl( - libc::PR_SET_SECCOMP, - libc::SECCOMP_MODE_FILTER, - bpf_prog_ptr, - ); - if rc != 0 { - return Err(InstallationError::Prctl(*libc::__errno_location())); - } - } - } - - Ok(()) -} -#[cfg(test)] -mod tests { - #![allow(clippy::undocumented_unsafe_blocks)] - - use std::collections::HashMap; - use std::sync::Arc; - use std::thread; - - use super::*; - use crate::common::BpfProgram; - - #[test] - fn test_deserialize_binary() { - // Malformed bincode binary. - { - let data = "adassafvc".to_string(); - deserialize_binary(data.as_bytes(), None).unwrap_err(); + // SAFETY: Safe as all args are correct. + unsafe { + let r = seccomp_arch_add(bpf_filter, arch.to_scmp_type()); + if r != 0 && r != MINUS_EEXIST { + return Err(CompilationError::LibSeccompArch); + } } - // Test that the binary deserialization is correct, and that the thread keys - // have been lowercased. - { - let bpf_prog = vec![ - sock_filter { - code: 32, - jt: 0, - jf: 0, - k: 0, - }, - sock_filter { - code: 32, - jt: 0, - jf: 0, - k: 4, - }, - ]; - let mut filter_map: HashMap = HashMap::new(); - filter_map.insert("VcpU".to_string(), bpf_prog.clone()); - let bytes = bincode::serialize(&filter_map).unwrap(); - - let mut expected_res = BpfThreadMap::new(); - expected_res.insert("vcpu".to_string(), Arc::new(bpf_prog)); - assert_eq!(deserialize_binary(&bytes[..], None).unwrap(), expected_res); + for rule in filter.filter.iter() { + // SAFETY: Safe as all args are correct. + let syscall = unsafe { + let r = seccomp_syscall_resolve_name(rule.syscall.as_ptr()); + if r == __NR_SCMP_ERROR { + return Err(CompilationError::LibSeccompSycall); + } + r + }; + + // TODO remove when we drop deprecated "basic" arg from cli. + // "basic" bpf means it ignores condition checks. + if basic { + // SAFETY: Safe as all args are correct. + unsafe { + if seccomp_rule_add(bpf_filter, filter_action, syscall, 0) != 0 { + return Err(CompilationError::LibSeccompRule); + } + } + } else if let Some(rules) = &rule.args { + let comparators = rules + .iter() + .map(|rule| rule.to_scmp_type()) + .collect::>(); + + // SAFETY: Safe as all args are correct. + // We can assume no one will define u32::MAX + // filters for a syscall. + #[allow(clippy::cast_possible_truncation)] + unsafe { + if seccomp_rule_add_array( + bpf_filter, + filter_action, + syscall, + comparators.len() as u32, + comparators.as_ptr(), + ) != 0 + { + return Err(CompilationError::LibSeccompRule); + } + } + } else { + // SAFETY: Safe as all args are correct. + unsafe { + if seccomp_rule_add(bpf_filter, filter_action, syscall, 0) != 0 { + return Err(CompilationError::LibSeccompRule); + } + } + } } - // Test deserialization with binary_limit. - { - let bpf_prog = vec![sock_filter { - code: 32, - jt: 0, - jf: 0, - k: 0, - }]; - - let mut filter_map: HashMap = HashMap::new(); - filter_map.insert("t1".to_string(), bpf_prog.clone()); - - let bytes = bincode::serialize(&filter_map).unwrap(); - - // Binary limit too low. - assert!(matches!( - deserialize_binary(&bytes[..], Some(20)).unwrap_err(), - DeserializationError::Bincode(error) - if error.to_string() == "the size limit has been reached" - )); - - let mut expected_res = BpfThreadMap::new(); - expected_res.insert("t1".to_string(), Arc::new(bpf_prog)); - - // Correct binary limit. - assert_eq!( - deserialize_binary(&bytes[..], Some(50)).unwrap(), - expected_res - ); + // SAFETY: Safe as all args are correect. + unsafe { + if seccomp_export_bpf(bpf_filter, memfd.as_raw_fd()) != 0 { + return Err(CompilationError::LibSeccompExport); + } } + memfd.rewind().map_err(CompilationError::MemfdRewind)?; + + // Cast is safe because usize == u64 + #[allow(clippy::cast_possible_truncation)] + let size = memfd.metadata().unwrap().size() as usize; + // Bpf instructions are 8 byte values and 4 byte alignment. + // We use u64 to satisfy these requirements. + let instructions = size / std::mem::size_of::(); + let mut bpf = vec![0_u64; instructions]; + + memfd + .read_exact(bpf.as_mut_bytes()) + .map_err(CompilationError::MemfdRead)?; + memfd.rewind().map_err(CompilationError::MemfdRewind)?; + + bpf_map.insert(name.clone(), bpf); } - #[test] - fn test_filter_apply() { - // Test filter too large. - thread::spawn(|| { - let filter: BpfProgram = vec![ - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: 0, - }; - 5000 // Limit is 4096 - ]; + let mut output_file = File::create(out_path).map_err(CompilationError::OutputCreate)?; - // Apply seccomp filter. - assert_eq!( - apply_filter(&filter).unwrap_err(), - InstallationError::FilterTooLarge - ); - }) - .join() - .unwrap(); - - // Test empty filter. - thread::spawn(|| { - let filter: BpfProgram = vec![]; - - assert_eq!(filter.len(), 0); - - let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) }; - assert_eq!(seccomp_level, 0); - - apply_filter(&filter).unwrap(); - - // test that seccomp level remains 0 on failure. - let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) }; - assert_eq!(seccomp_level, 0); - }) - .join() - .unwrap(); - - // Test invalid BPF code. - thread::spawn(|| { - let filter = vec![sock_filter { - // invalid opcode - code: 9999, - jt: 0, - jf: 0, - k: 0, - }]; - - let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) }; - assert_eq!(seccomp_level, 0); - - assert_eq!( - apply_filter(&filter).unwrap_err(), - InstallationError::Prctl(22) - ); - - // test that seccomp level remains 0 on failure. - let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) }; - assert_eq!(seccomp_level, 0); - }) - .join() - .unwrap(); - } + bincode::encode_into_std_write(&bpf_map, &mut output_file, BINCODE_CONFIG) + .map_err(CompilationError::BincodeSerialize)?; + Ok(()) } diff --git a/src/seccompiler/src/seccompiler_bin.rs b/src/seccompiler/src/seccompiler_bin.rs deleted file mode 100644 index 890a2a3ecdb..00000000000 --- a/src/seccompiler/src/seccompiler_bin.rs +++ /dev/null @@ -1,578 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -//! seccompiler-bin is a program that compiles multi-threaded seccomp-bpf filters expressed as JSON -//! into raw BPF programs, serializing them and outputting them to a file. -//! -//! Used in conjunction with the provided library crate, one can deserialize the binary filters -//! and easily install them on a per-thread basis, in order to achieve a quick and robust -//! seccomp-based jailing solution. -//! -//! See the documentation on github for more information. -//! -//! ```text -//! The compilation goes through a couple of steps, from JSON to BPF: -//! -//! JSON -//! | -//! (via serde_json) -//! | -//! V -//! collection of `Filter` objects -//! | -//! (via Compiler.compile_blob(...)) -//! | -//! V -//! collection of `SeccompFilter` objects -//! (IR - intermediate representation) -//! | -//! (via SeccompFilter.try_into::(...)) -//! | -//! V -//! collection of `BpfProgram` objects -//! ``` - -use std::collections::BTreeMap; -use std::convert::TryInto; -use std::fs::File; -use std::io::BufReader; -use std::path::PathBuf; - -mod backend; -mod common; -mod compiler; -mod syscall_table; - -use backend::{TargetArch, TargetArchError}; -use bincode::Error as BincodeError; -use common::BpfProgram; -use compiler::{CompilationError, Compiler, JsonFile}; -use serde_json::error::Error as JSONError; -use utils::arg_parser::{ - ArgParser, Argument, Arguments as ArgumentsBag, UtilsArgParserError as ArgParserError, -}; - -const SECCOMPILER_VERSION: &str = env!("CARGO_PKG_VERSION"); -const DEFAULT_OUTPUT_FILENAME: &str = "seccomp_binary_filter.out"; - -#[derive(Debug, thiserror::Error)] -enum SeccompError { - #[error("Bincode (de)serialization failed: {0}")] - Bincode(BincodeError), - #[error("{0}")] - Compilation(CompilationError), - #[error("{}", format!("Failed to open file {:?}: {1}", .0, .1).replace('\"', ""))] - FileOpen(PathBuf, std::io::Error), - #[error("Error parsing JSON: {0}")] - Json(JSONError), - #[error("Missing input file.")] - MissingInputFile, - #[error("Missing target arch.")] - MissingTargetArch, - #[error("{0}")] - Arch(#[from] TargetArchError), -} - -#[derive(Debug, PartialEq)] -struct Arguments { - input_file: String, - output_file: String, - target_arch: TargetArch, - is_basic: bool, -} - -fn build_arg_parser() -> ArgParser<'static> { - ArgParser::new() - .arg( - Argument::new("input-file") - .required(true) - .takes_value(true) - .help("File path of the JSON input."), - ) - .arg( - Argument::new("output-file") - .required(false) - .takes_value(true) - .default_value(DEFAULT_OUTPUT_FILENAME) - .help("Optional path of the output file."), - ) - .arg( - Argument::new("target-arch") - .required(true) - .takes_value(true) - .help( - "The computer architecture where the BPF program runs. Supported \ - architectures: x86_64, aarch64.", - ), - ) - .arg(Argument::new("basic").takes_value(false).help( - "Deprecated! Transforms the filters into basic filters. Drops all argument checks and \ - rule-level actions. Not recommended.", - )) -} - -fn get_argument_values(arguments: &ArgumentsBag) -> Result { - let Some(arch_string) = arguments.single_value("target-arch") else { - return Err(SeccompError::MissingTargetArch); - }; - let target_arch: TargetArch = arch_string.as_str().try_into()?; - - let Some(input_file) = arguments.single_value("input-file") else { - return Err(SeccompError::MissingInputFile); - }; - - let is_basic = arguments.flag_present("basic"); - if is_basic { - println!( - "Warning! You are using a deprecated parameter: --basic, that will be removed in a \ - future version.\n" - ); - } - - Ok(Arguments { - target_arch, - input_file: input_file.to_owned(), - // Safe to unwrap because it has a default value - output_file: arguments.single_value("output-file").unwrap().to_owned(), - is_basic, - }) -} - -fn compile(args: &Arguments) -> Result<(), SeccompError> { - let input_file = File::open(&args.input_file) - .map_err(|err| SeccompError::FileOpen(PathBuf::from(&args.input_file), err))?; - let mut input_reader = BufReader::new(input_file); - let filters = - serde_json::from_reader::<_, JsonFile>(&mut input_reader).map_err(SeccompError::Json)?; - let compiler = Compiler::new(args.target_arch); - - // transform the IR into a Map of BPFPrograms - let bpf_data: BTreeMap = compiler - .compile_blob(filters.0, args.is_basic) - .map_err(SeccompError::Compilation)?; - - // serialize the BPF programs & output them to a file - let output_file = File::create(&args.output_file) - .map_err(|err| SeccompError::FileOpen(PathBuf::from(&args.output_file), err))?; - bincode::serialize_into(output_file, &bpf_data).map_err(SeccompError::Bincode)?; - - Ok(()) -} - -#[derive(Debug, thiserror::Error, displaydoc::Display)] -enum SeccompilerError { - /// Argument Parsing Error: {0} - ArgParsing(ArgParserError), - /// {0} \n\nFor more information try --help. - InvalidArgumentValue(SeccompError), - /// {0} - Error(SeccompError), -} - -fn main() -> core::result::Result<(), SeccompilerError> { - let result = main_exec(); - if let Err(e) = result { - eprintln!("{}", e); - Err(e) - } else { - Ok(()) - } -} - -fn main_exec() -> core::result::Result<(), SeccompilerError> { - let mut arg_parser = build_arg_parser(); - - arg_parser - .parse_from_cmdline() - .map_err(SeccompilerError::ArgParsing)?; - - if arg_parser.arguments().flag_present("help") { - println!("Seccompiler-bin v{}\n", SECCOMPILER_VERSION); - println!("{}", arg_parser.formatted_help()); - return Ok(()); - } - if arg_parser.arguments().flag_present("version") { - println!("Seccompiler-bin v{}\n", SECCOMPILER_VERSION); - return Ok(()); - } - - let args = get_argument_values(arg_parser.arguments()) - .map_err(SeccompilerError::InvalidArgumentValue)?; - - compile(&args).map_err(SeccompilerError::Error)?; - - println!("Filter successfully compiled into: {}", args.output_file); - Ok(()) -} - -#[cfg(test)] -mod tests { - #![allow(clippy::undocumented_unsafe_blocks)] - - use std::io; - use std::io::Write; - use std::path::PathBuf; - - use bincode::Error as BincodeError; - use vmm_sys_util::tempfile::TempFile; - - use super::compiler::CompilationError as FilterFormatError; - use super::{ - build_arg_parser, compile, get_argument_values, Arguments, SeccompError, - DEFAULT_OUTPUT_FILENAME, - }; - use crate::backend::{TargetArch, TargetArchError}; - - // Correct JSON input data - static CORRECT_JSON_INPUT: &str = r#" - { - "thread_1": { - "default_action": { - "errno": 12 - }, - "filter_action": "allow", - "filter": [ - { - "syscall": "open" - }, - { - "syscall": "close" - }, - { - "syscall": "stat" - }, - { - "syscall": "futex", - "args": [ - { - "index": 2, - "type": "dword", - "op": "le", - "val": 65 - }, - { - "index": 1, - "type": "qword", - "op": "ne", - "val": 80 - } - ] - }, - { - "syscall": "futex", - "args": [ - { - "index": 3, - "type": "qword", - "op": "gt", - "val": 65 - }, - { - "index": 1, - "type": "qword", - "op": "lt", - "val": 80 - } - ] - }, - { - "syscall": "futex", - "args": [ - { - "index": 3, - "type": "qword", - "op": "ge", - "val": 65 - } - ] - }, - { - "syscall": "ioctl", - "args": [ - { - "index": 3, - "type": "dword", - "op": { - "masked_eq": 100 - }, - "val": 65 - } - ] - } - ] - }, - "thread_2": { - "default_action": "trap", - "filter_action": "allow", - "filter": [ - { - "syscall": "ioctl", - "args": [ - { - "index": 3, - "type": "dword", - "op": "eq", - "val": 65 - } - ] - } - ] - } - } - "#; - - #[test] - fn test_error_messages() { - let path = PathBuf::from("/path"); - assert_eq!( - format!( - "{}", - SeccompError::Bincode(BincodeError::new(bincode::ErrorKind::SizeLimit)) - ), - format!( - "Bincode (de)serialization failed: {}", - BincodeError::new(bincode::ErrorKind::SizeLimit) - ) - ); - assert_eq!( - format!( - "{}", - SeccompError::Compilation(FilterFormatError::SyscallName( - "dsaa".to_string(), - TargetArch::aarch64 - )) - ), - format!( - "{}", - FilterFormatError::SyscallName("dsaa".to_string(), TargetArch::aarch64) - ) - ); - assert_eq!( - format!( - "{}", - SeccompError::FileOpen(path.clone(), io::Error::from_raw_os_error(2)) - ), - format!( - "Failed to open file {:?}: {}", - path, - io::Error::from_raw_os_error(2) - ) - .replace('\"', "") - ); - assert_eq!( - format!( - "{}", - SeccompError::Json(serde_json::from_str::("").unwrap_err()) - ), - format!( - "Error parsing JSON: {}", - serde_json::from_str::("").unwrap_err() - ) - ); - assert_eq!( - format!("{}", SeccompError::MissingInputFile), - "Missing input file." - ); - assert_eq!( - format!("{}", SeccompError::MissingTargetArch), - "Missing target arch." - ); - assert_eq!( - format!( - "{}", - SeccompError::Arch(TargetArchError::InvalidString("lala".to_string())) - ), - format!("{}", TargetArchError::InvalidString("lala".to_string())) - ); - } - - #[test] - fn test_get_argument_values() { - let arg_parser = build_arg_parser(); - // correct arguments - let arguments = &mut arg_parser.arguments().clone(); - arguments - .parse( - vec![ - "seccompiler-bin", - "--input-file", - "foo.txt", - "--target-arch", - "x86_64", - ] - .into_iter() - .map(String::from) - .collect::>() - .as_ref(), - ) - .unwrap(); - assert_eq!( - get_argument_values(arguments).unwrap(), - Arguments { - input_file: "foo.txt".to_string(), - output_file: DEFAULT_OUTPUT_FILENAME.to_string(), - target_arch: TargetArch::x86_64, - is_basic: false, - } - ); - - let arguments = &mut arg_parser.arguments().clone(); - arguments - .parse( - vec![ - "seccompiler-bin", - "--input-file", - "foo.txt", - "--target-arch", - "x86_64", - "--output-file", - "/path.to/file.txt", - "--basic", - ] - .into_iter() - .map(String::from) - .collect::>() - .as_ref(), - ) - .unwrap(); - assert_eq!( - get_argument_values(arguments).unwrap(), - Arguments { - input_file: "foo.txt".to_string(), - output_file: "/path.to/file.txt".to_string(), - target_arch: TargetArch::x86_64, - is_basic: true - } - ); - - // no args - let arguments = &mut arg_parser.arguments().clone(); - assert!(arguments - .parse( - vec!["seccompiler-bin"] - .into_iter() - .map(String::from) - .collect::>() - .as_ref(), - ) - .is_err()); - - // missing --target-arch - let arguments = &mut arg_parser.arguments().clone(); - assert!(arguments - .parse( - vec!["seccompiler-bin", "--input-file", "foo.txt"] - .into_iter() - .map(String::from) - .collect::>() - .as_ref(), - ) - .is_err()); - - // missing --input-file - let arguments = &mut arg_parser.arguments().clone(); - assert!(arguments - .parse( - vec!["seccompiler-bin", "--target-arch", "x86_64"] - .into_iter() - .map(String::from) - .collect::>() - .as_ref(), - ) - .is_err()); - - // invalid --target-arch - let arguments = &mut arg_parser.arguments().clone(); - arguments - .parse( - vec![ - "seccompiler-bin", - "--input-file", - "foo.txt", - "--target-arch", - "x86_64das", - "--output-file", - "/path.to/file.txt", - ] - .into_iter() - .map(String::from) - .collect::>() - .as_ref(), - ) - .unwrap(); - get_argument_values(arguments).unwrap_err(); - - // invalid value supplied to --basic - let arguments = &mut arg_parser.arguments().clone(); - assert!(arguments - .parse( - vec![ - "seccompiler-bin", - "--input-file", - "foo.txt", - "--target-arch", - "x86_64", - "--basic", - "invalid", - ] - .into_iter() - .map(String::from) - .collect::>() - .as_ref(), - ) - .is_err()); - } - - #[allow(clippy::useless_asref)] - #[test] - fn test_compile() { - // --input-file was deleted - { - let mut in_file = TempFile::new().unwrap(); - in_file.remove().unwrap(); - let args = Arguments { - input_file: in_file.as_path().to_str().unwrap().to_string(), - target_arch: TargetArch::x86_64, - output_file: "bpf.out".to_string(), - is_basic: false, - }; - - match compile(&args).unwrap_err() { - SeccompError::FileOpen(buf, _) => assert_eq!(buf, PathBuf::from(in_file.as_path())), - _ => panic!("Expected FileOpen error."), - } - } - - // test a successful compilation - { - let in_file = TempFile::new().unwrap(); - let out_file = TempFile::new().unwrap(); - - in_file - .as_file() - .write_all(CORRECT_JSON_INPUT.as_bytes()) - .unwrap(); - - let arguments = Arguments { - input_file: in_file.as_path().to_str().unwrap().to_string(), - output_file: out_file.as_path().to_str().unwrap().to_string(), - target_arch: TargetArch::x86_64, - is_basic: false, - }; - - // do the compilation & check for errors - compile(&arguments).unwrap(); - - // also check with is_basic: true - let arguments = Arguments { - input_file: in_file.as_path().to_str().unwrap().to_string(), - output_file: out_file.as_path().to_str().unwrap().to_string(), - target_arch: TargetArch::x86_64, - is_basic: true, - }; - - // do the compilation & check for errors - compile(&arguments).unwrap(); - } - } -} diff --git a/src/seccompiler/src/syscall_table/aarch64.rs b/src/seccompiler/src/syscall_table/aarch64.rs deleted file mode 100644 index 386d09b78d3..00000000000 --- a/src/seccompiler/src/syscall_table/aarch64.rs +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// This file is auto-generated by `tools/devtool generate_syscall_tables`. -// Do NOT manually edit! -// Generated at: Mon 15 Nov 11:41:50 UTC 2021 -// Kernel version: 5.10 - -use std::collections::HashMap; - -pub fn make_syscall_table(map: &mut HashMap) { - map.insert("accept4".to_string(), 242); - map.insert("accept".to_string(), 202); - map.insert("acct".to_string(), 89); - map.insert("add_key".to_string(), 217); - map.insert("adjtimex".to_string(), 171); - map.insert("bind".to_string(), 200); - map.insert("bpf".to_string(), 280); - map.insert("brk".to_string(), 214); - map.insert("capget".to_string(), 90); - map.insert("capset".to_string(), 91); - map.insert("chdir".to_string(), 49); - map.insert("chroot".to_string(), 51); - map.insert("clock_adjtime".to_string(), 266); - map.insert("clock_getres".to_string(), 114); - map.insert("clock_gettime".to_string(), 113); - map.insert("clock_nanosleep".to_string(), 115); - map.insert("clock_settime".to_string(), 112); - map.insert("clone3".to_string(), 435); - map.insert("clone".to_string(), 220); - map.insert("close_range".to_string(), 436); - map.insert("close".to_string(), 57); - map.insert("connect".to_string(), 203); - map.insert("copy_file_range".to_string(), 285); - map.insert("delete_module".to_string(), 106); - map.insert("dup3".to_string(), 24); - map.insert("dup".to_string(), 23); - map.insert("epoll_create1".to_string(), 20); - map.insert("epoll_ctl".to_string(), 21); - map.insert("epoll_pwait".to_string(), 22); - map.insert("eventfd2".to_string(), 19); - map.insert("execveat".to_string(), 281); - map.insert("execve".to_string(), 221); - map.insert("exit_group".to_string(), 94); - map.insert("exit".to_string(), 93); - map.insert("faccessat2".to_string(), 439); - map.insert("faccessat".to_string(), 48); - map.insert("fadvise64".to_string(), 223); - map.insert("fallocate".to_string(), 47); - map.insert("fanotify_init".to_string(), 262); - map.insert("fanotify_mark".to_string(), 263); - map.insert("fchdir".to_string(), 50); - map.insert("fchmodat".to_string(), 53); - map.insert("fchmod".to_string(), 52); - map.insert("fchownat".to_string(), 54); - map.insert("fchown".to_string(), 55); - map.insert("fcntl".to_string(), 25); - map.insert("fdatasync".to_string(), 83); - map.insert("fgetxattr".to_string(), 10); - map.insert("finit_module".to_string(), 273); - map.insert("flistxattr".to_string(), 13); - map.insert("flock".to_string(), 32); - map.insert("fremovexattr".to_string(), 16); - map.insert("fsconfig".to_string(), 431); - map.insert("fsetxattr".to_string(), 7); - map.insert("fsmount".to_string(), 432); - map.insert("fsopen".to_string(), 430); - map.insert("fspick".to_string(), 433); - map.insert("fstatfs".to_string(), 44); - map.insert("fstat".to_string(), 80); - map.insert("fsync".to_string(), 82); - map.insert("ftruncate".to_string(), 46); - map.insert("futex".to_string(), 98); - map.insert("getcpu".to_string(), 168); - map.insert("getcwd".to_string(), 17); - map.insert("getdents64".to_string(), 61); - map.insert("getegid".to_string(), 177); - map.insert("geteuid".to_string(), 175); - map.insert("getgid".to_string(), 176); - map.insert("getgroups".to_string(), 158); - map.insert("getitimer".to_string(), 102); - map.insert("get_mempolicy".to_string(), 236); - map.insert("getpeername".to_string(), 205); - map.insert("getpgid".to_string(), 155); - map.insert("getpid".to_string(), 172); - map.insert("getppid".to_string(), 173); - map.insert("getpriority".to_string(), 141); - map.insert("getrandom".to_string(), 278); - map.insert("getresgid".to_string(), 150); - map.insert("getresuid".to_string(), 148); - map.insert("getrlimit".to_string(), 163); - map.insert("get_robust_list".to_string(), 100); - map.insert("getrusage".to_string(), 165); - map.insert("getsid".to_string(), 156); - map.insert("getsockname".to_string(), 204); - map.insert("getsockopt".to_string(), 209); - map.insert("gettid".to_string(), 178); - map.insert("gettimeofday".to_string(), 169); - map.insert("getuid".to_string(), 174); - map.insert("getxattr".to_string(), 8); - map.insert("init_module".to_string(), 105); - map.insert("inotify_add_watch".to_string(), 27); - map.insert("inotify_init1".to_string(), 26); - map.insert("inotify_rm_watch".to_string(), 28); - map.insert("io_cancel".to_string(), 3); - map.insert("ioctl".to_string(), 29); - map.insert("io_destroy".to_string(), 1); - map.insert("io_getevents".to_string(), 4); - map.insert("io_pgetevents".to_string(), 292); - map.insert("ioprio_get".to_string(), 31); - map.insert("ioprio_set".to_string(), 30); - map.insert("io_setup".to_string(), 0); - map.insert("io_submit".to_string(), 2); - map.insert("io_uring_enter".to_string(), 426); - map.insert("io_uring_register".to_string(), 427); - map.insert("io_uring_setup".to_string(), 425); - map.insert("kcmp".to_string(), 272); - map.insert("kexec_file_load".to_string(), 294); - map.insert("kexec_load".to_string(), 104); - map.insert("keyctl".to_string(), 219); - map.insert("kill".to_string(), 129); - map.insert("lgetxattr".to_string(), 9); - map.insert("linkat".to_string(), 37); - map.insert("listen".to_string(), 201); - map.insert("listxattr".to_string(), 11); - map.insert("llistxattr".to_string(), 12); - map.insert("lookup_dcookie".to_string(), 18); - map.insert("lremovexattr".to_string(), 15); - map.insert("lseek".to_string(), 62); - map.insert("lsetxattr".to_string(), 6); - map.insert("madvise".to_string(), 233); - map.insert("mbind".to_string(), 235); - map.insert("membarrier".to_string(), 283); - map.insert("memfd_create".to_string(), 279); - map.insert("migrate_pages".to_string(), 238); - map.insert("mincore".to_string(), 232); - map.insert("mkdirat".to_string(), 34); - map.insert("mknodat".to_string(), 33); - map.insert("mlock2".to_string(), 284); - map.insert("mlockall".to_string(), 230); - map.insert("mlock".to_string(), 228); - map.insert("mmap".to_string(), 222); - map.insert("mount".to_string(), 40); - map.insert("move_mount".to_string(), 429); - map.insert("move_pages".to_string(), 239); - map.insert("mprotect".to_string(), 226); - map.insert("mq_getsetattr".to_string(), 185); - map.insert("mq_notify".to_string(), 184); - map.insert("mq_open".to_string(), 180); - map.insert("mq_timedreceive".to_string(), 183); - map.insert("mq_timedsend".to_string(), 182); - map.insert("mq_unlink".to_string(), 181); - map.insert("mremap".to_string(), 216); - map.insert("msgctl".to_string(), 187); - map.insert("msgget".to_string(), 186); - map.insert("msgrcv".to_string(), 188); - map.insert("msgsnd".to_string(), 189); - map.insert("msync".to_string(), 227); - map.insert("munlockall".to_string(), 231); - map.insert("munlock".to_string(), 229); - map.insert("munmap".to_string(), 215); - map.insert("name_to_handle_at".to_string(), 264); - map.insert("nanosleep".to_string(), 101); - map.insert("newfstatat".to_string(), 79); - map.insert("nfsservctl".to_string(), 42); - map.insert("openat2".to_string(), 437); - map.insert("openat".to_string(), 56); - map.insert("open_by_handle_at".to_string(), 265); - map.insert("open_tree".to_string(), 428); - map.insert("perf_event_open".to_string(), 241); - map.insert("personality".to_string(), 92); - map.insert("pidfd_getfd".to_string(), 438); - map.insert("pidfd_open".to_string(), 434); - map.insert("pidfd_send_signal".to_string(), 424); - map.insert("pipe2".to_string(), 59); - map.insert("pivot_root".to_string(), 41); - map.insert("pkey_alloc".to_string(), 289); - map.insert("pkey_free".to_string(), 290); - map.insert("pkey_mprotect".to_string(), 288); - map.insert("ppoll".to_string(), 73); - map.insert("prctl".to_string(), 167); - map.insert("pread64".to_string(), 67); - map.insert("preadv2".to_string(), 286); - map.insert("preadv".to_string(), 69); - map.insert("prlimit64".to_string(), 261); - map.insert("process_madvise".to_string(), 440); - map.insert("process_vm_readv".to_string(), 270); - map.insert("process_vm_writev".to_string(), 271); - map.insert("pselect6".to_string(), 72); - map.insert("ptrace".to_string(), 117); - map.insert("pwrite64".to_string(), 68); - map.insert("pwritev2".to_string(), 287); - map.insert("pwritev".to_string(), 70); - map.insert("quotactl".to_string(), 60); - map.insert("readahead".to_string(), 213); - map.insert("readlinkat".to_string(), 78); - map.insert("read".to_string(), 63); - map.insert("readv".to_string(), 65); - map.insert("reboot".to_string(), 142); - map.insert("recvfrom".to_string(), 207); - map.insert("recvmmsg".to_string(), 243); - map.insert("recvmsg".to_string(), 212); - map.insert("remap_file_pages".to_string(), 234); - map.insert("removexattr".to_string(), 14); - map.insert("renameat2".to_string(), 276); - map.insert("renameat".to_string(), 38); - map.insert("request_key".to_string(), 218); - map.insert("restart_syscall".to_string(), 128); - map.insert("rseq".to_string(), 293); - map.insert("rt_sigaction".to_string(), 134); - map.insert("rt_sigpending".to_string(), 136); - map.insert("rt_sigprocmask".to_string(), 135); - map.insert("rt_sigqueueinfo".to_string(), 138); - map.insert("rt_sigreturn".to_string(), 139); - map.insert("rt_sigsuspend".to_string(), 133); - map.insert("rt_sigtimedwait".to_string(), 137); - map.insert("rt_tgsigqueueinfo".to_string(), 240); - map.insert("sched_getaffinity".to_string(), 123); - map.insert("sched_getattr".to_string(), 275); - map.insert("sched_getparam".to_string(), 121); - map.insert("sched_get_priority_max".to_string(), 125); - map.insert("sched_get_priority_min".to_string(), 126); - map.insert("sched_getscheduler".to_string(), 120); - map.insert("sched_rr_get_interval".to_string(), 127); - map.insert("sched_setaffinity".to_string(), 122); - map.insert("sched_setattr".to_string(), 274); - map.insert("sched_setparam".to_string(), 118); - map.insert("sched_setscheduler".to_string(), 119); - map.insert("sched_yield".to_string(), 124); - map.insert("seccomp".to_string(), 277); - map.insert("semctl".to_string(), 191); - map.insert("semget".to_string(), 190); - map.insert("semop".to_string(), 193); - map.insert("semtimedop".to_string(), 192); - map.insert("sendfile".to_string(), 71); - map.insert("sendmmsg".to_string(), 269); - map.insert("sendmsg".to_string(), 211); - map.insert("sendto".to_string(), 206); - map.insert("setdomainname".to_string(), 162); - map.insert("setfsgid".to_string(), 152); - map.insert("setfsuid".to_string(), 151); - map.insert("setgid".to_string(), 144); - map.insert("setgroups".to_string(), 159); - map.insert("sethostname".to_string(), 161); - map.insert("setitimer".to_string(), 103); - map.insert("set_mempolicy".to_string(), 237); - map.insert("setns".to_string(), 268); - map.insert("setpgid".to_string(), 154); - map.insert("setpriority".to_string(), 140); - map.insert("setregid".to_string(), 143); - map.insert("setresgid".to_string(), 149); - map.insert("setresuid".to_string(), 147); - map.insert("setreuid".to_string(), 145); - map.insert("setrlimit".to_string(), 164); - map.insert("set_robust_list".to_string(), 99); - map.insert("setsid".to_string(), 157); - map.insert("setsockopt".to_string(), 208); - map.insert("set_tid_address".to_string(), 96); - map.insert("settimeofday".to_string(), 170); - map.insert("setuid".to_string(), 146); - map.insert("setxattr".to_string(), 5); - map.insert("shmat".to_string(), 196); - map.insert("shmctl".to_string(), 195); - map.insert("shmdt".to_string(), 197); - map.insert("shmget".to_string(), 194); - map.insert("shutdown".to_string(), 210); - map.insert("sigaltstack".to_string(), 132); - map.insert("signalfd4".to_string(), 74); - map.insert("socketpair".to_string(), 199); - map.insert("socket".to_string(), 198); - map.insert("splice".to_string(), 76); - map.insert("statfs".to_string(), 43); - map.insert("statx".to_string(), 291); - map.insert("swapoff".to_string(), 225); - map.insert("swapon".to_string(), 224); - map.insert("symlinkat".to_string(), 36); - map.insert("sync_file_range".to_string(), 84); - map.insert("syncfs".to_string(), 267); - map.insert("sync".to_string(), 81); - map.insert("sysinfo".to_string(), 179); - map.insert("syslog".to_string(), 116); - map.insert("tee".to_string(), 77); - map.insert("tgkill".to_string(), 131); - map.insert("timer_create".to_string(), 107); - map.insert("timer_delete".to_string(), 111); - map.insert("timerfd_create".to_string(), 85); - map.insert("timerfd_gettime".to_string(), 87); - map.insert("timerfd_settime".to_string(), 86); - map.insert("timer_getoverrun".to_string(), 109); - map.insert("timer_gettime".to_string(), 108); - map.insert("timer_settime".to_string(), 110); - map.insert("times".to_string(), 153); - map.insert("tkill".to_string(), 130); - map.insert("truncate".to_string(), 45); - map.insert("umask".to_string(), 166); - map.insert("umount2".to_string(), 39); - map.insert("uname".to_string(), 160); - map.insert("unlinkat".to_string(), 35); - map.insert("unshare".to_string(), 97); - map.insert("userfaultfd".to_string(), 282); - map.insert("utimensat".to_string(), 88); - map.insert("vhangup".to_string(), 58); - map.insert("vmsplice".to_string(), 75); - map.insert("wait4".to_string(), 260); - map.insert("waitid".to_string(), 95); - map.insert("write".to_string(), 64); - map.insert("writev".to_string(), 66); -} diff --git a/src/seccompiler/src/syscall_table/mod.rs b/src/seccompiler/src/syscall_table/mod.rs deleted file mode 100644 index 3dca50c748d..00000000000 --- a/src/seccompiler/src/syscall_table/mod.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -mod aarch64; -mod x86_64; - -use std::collections::HashMap; - -use crate::backend::TargetArch; - -/// Creates and owns a mapping from the arch-specific syscall name to the right number. -#[derive(Debug)] -pub struct SyscallTable { - map: HashMap, - arch: TargetArch, -} - -/// Number of syscalls for x86_64 (rough upper bound). -const MAP_CAPACITY: usize = 351; - -impl SyscallTable { - /// Create new syscall table - pub fn new(arch: TargetArch) -> Self { - let mut instance = Self { - arch, - map: HashMap::with_capacity(MAP_CAPACITY), - }; - - instance.populate_map(); - - instance - } - - /// Returns the arch-specific syscall number based on the given name. - pub fn get_syscall_nr(&self, sys_name: &str) -> Option { - self.map.get(sys_name).copied() - } - - /// Populates the arch-specific syscall map. - fn populate_map(&mut self) { - match self.arch { - TargetArch::aarch64 => aarch64::make_syscall_table(&mut self.map), - TargetArch::x86_64 => x86_64::make_syscall_table(&mut self.map), - } - } -} - -#[cfg(test)] -mod tests { - use super::SyscallTable; - use crate::backend::TargetArch; - - #[test] - fn test_get_syscall_nr() { - // get number for a valid syscall - let instance_x86_64 = SyscallTable::new(TargetArch::x86_64); - let instance_aarch64 = SyscallTable::new(TargetArch::aarch64); - - assert_eq!(instance_x86_64.get_syscall_nr("close").unwrap(), 3); - assert_eq!(instance_aarch64.get_syscall_nr("close").unwrap(), 57); - - // invalid syscall name - assert!(instance_x86_64.get_syscall_nr("nosyscall").is_none()); - assert!(instance_aarch64.get_syscall_nr("nosyscall").is_none()); - } -} diff --git a/src/seccompiler/src/syscall_table/x86_64.rs b/src/seccompiler/src/syscall_table/x86_64.rs deleted file mode 100644 index 9350bd5ce57..00000000000 --- a/src/seccompiler/src/syscall_table/x86_64.rs +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// This file is auto-generated by `tools/devtool generate_syscall_tables`. -// Do NOT manually edit! -// Generated at: Mon 15 Nov 11:41:50 UTC 2021 -// Kernel version: 5.10 - -use std::collections::HashMap; - -pub fn make_syscall_table(map: &mut HashMap) { - map.insert("accept4".to_string(), 288); - map.insert("accept".to_string(), 43); - map.insert("access".to_string(), 21); - map.insert("acct".to_string(), 163); - map.insert("add_key".to_string(), 248); - map.insert("adjtimex".to_string(), 159); - map.insert("afs_syscall".to_string(), 183); - map.insert("alarm".to_string(), 37); - map.insert("arch_prctl".to_string(), 158); - map.insert("bind".to_string(), 49); - map.insert("bpf".to_string(), 321); - map.insert("brk".to_string(), 12); - map.insert("capget".to_string(), 125); - map.insert("capset".to_string(), 126); - map.insert("chdir".to_string(), 80); - map.insert("chmod".to_string(), 90); - map.insert("chown".to_string(), 92); - map.insert("chroot".to_string(), 161); - map.insert("clock_adjtime".to_string(), 305); - map.insert("clock_getres".to_string(), 229); - map.insert("clock_gettime".to_string(), 228); - map.insert("clock_nanosleep".to_string(), 230); - map.insert("clock_settime".to_string(), 227); - map.insert("clone3".to_string(), 435); - map.insert("clone".to_string(), 56); - map.insert("close_range".to_string(), 436); - map.insert("close".to_string(), 3); - map.insert("connect".to_string(), 42); - map.insert("copy_file_range".to_string(), 326); - map.insert("create_module".to_string(), 174); - map.insert("creat".to_string(), 85); - map.insert("delete_module".to_string(), 176); - map.insert("dup2".to_string(), 33); - map.insert("dup3".to_string(), 292); - map.insert("dup".to_string(), 32); - map.insert("epoll_create1".to_string(), 291); - map.insert("epoll_create".to_string(), 213); - map.insert("epoll_ctl_old".to_string(), 214); - map.insert("epoll_ctl".to_string(), 233); - map.insert("epoll_pwait".to_string(), 281); - map.insert("epoll_wait_old".to_string(), 215); - map.insert("epoll_wait".to_string(), 232); - map.insert("eventfd2".to_string(), 290); - map.insert("eventfd".to_string(), 284); - map.insert("execveat".to_string(), 322); - map.insert("execve".to_string(), 59); - map.insert("exit_group".to_string(), 231); - map.insert("exit".to_string(), 60); - map.insert("faccessat2".to_string(), 439); - map.insert("faccessat".to_string(), 269); - map.insert("fadvise64".to_string(), 221); - map.insert("fallocate".to_string(), 285); - map.insert("fanotify_init".to_string(), 300); - map.insert("fanotify_mark".to_string(), 301); - map.insert("fchdir".to_string(), 81); - map.insert("fchmodat".to_string(), 268); - map.insert("fchmod".to_string(), 91); - map.insert("fchownat".to_string(), 260); - map.insert("fchown".to_string(), 93); - map.insert("fcntl".to_string(), 72); - map.insert("fdatasync".to_string(), 75); - map.insert("fgetxattr".to_string(), 193); - map.insert("finit_module".to_string(), 313); - map.insert("flistxattr".to_string(), 196); - map.insert("flock".to_string(), 73); - map.insert("fork".to_string(), 57); - map.insert("fremovexattr".to_string(), 199); - map.insert("fsconfig".to_string(), 431); - map.insert("fsetxattr".to_string(), 190); - map.insert("fsmount".to_string(), 432); - map.insert("fsopen".to_string(), 430); - map.insert("fspick".to_string(), 433); - map.insert("fstatfs".to_string(), 138); - map.insert("fstat".to_string(), 5); - map.insert("fsync".to_string(), 74); - map.insert("ftruncate".to_string(), 77); - map.insert("futex".to_string(), 202); - map.insert("futimesat".to_string(), 261); - map.insert("getcpu".to_string(), 309); - map.insert("getcwd".to_string(), 79); - map.insert("getdents64".to_string(), 217); - map.insert("getdents".to_string(), 78); - map.insert("getegid".to_string(), 108); - map.insert("geteuid".to_string(), 107); - map.insert("getgid".to_string(), 104); - map.insert("getgroups".to_string(), 115); - map.insert("getitimer".to_string(), 36); - map.insert("get_kernel_syms".to_string(), 177); - map.insert("get_mempolicy".to_string(), 239); - map.insert("getpeername".to_string(), 52); - map.insert("getpgid".to_string(), 121); - map.insert("getpgrp".to_string(), 111); - map.insert("getpid".to_string(), 39); - map.insert("getpmsg".to_string(), 181); - map.insert("getppid".to_string(), 110); - map.insert("getpriority".to_string(), 140); - map.insert("getrandom".to_string(), 318); - map.insert("getresgid".to_string(), 120); - map.insert("getresuid".to_string(), 118); - map.insert("getrlimit".to_string(), 97); - map.insert("get_robust_list".to_string(), 274); - map.insert("getrusage".to_string(), 98); - map.insert("getsid".to_string(), 124); - map.insert("getsockname".to_string(), 51); - map.insert("getsockopt".to_string(), 55); - map.insert("get_thread_area".to_string(), 211); - map.insert("gettid".to_string(), 186); - map.insert("gettimeofday".to_string(), 96); - map.insert("getuid".to_string(), 102); - map.insert("getxattr".to_string(), 191); - map.insert("init_module".to_string(), 175); - map.insert("inotify_add_watch".to_string(), 254); - map.insert("inotify_init1".to_string(), 294); - map.insert("inotify_init".to_string(), 253); - map.insert("inotify_rm_watch".to_string(), 255); - map.insert("io_cancel".to_string(), 210); - map.insert("ioctl".to_string(), 16); - map.insert("io_destroy".to_string(), 207); - map.insert("io_getevents".to_string(), 208); - map.insert("ioperm".to_string(), 173); - map.insert("io_pgetevents".to_string(), 333); - map.insert("iopl".to_string(), 172); - map.insert("ioprio_get".to_string(), 252); - map.insert("ioprio_set".to_string(), 251); - map.insert("io_setup".to_string(), 206); - map.insert("io_submit".to_string(), 209); - map.insert("io_uring_enter".to_string(), 426); - map.insert("io_uring_register".to_string(), 427); - map.insert("io_uring_setup".to_string(), 425); - map.insert("kcmp".to_string(), 312); - map.insert("kexec_file_load".to_string(), 320); - map.insert("kexec_load".to_string(), 246); - map.insert("keyctl".to_string(), 250); - map.insert("kill".to_string(), 62); - map.insert("lchown".to_string(), 94); - map.insert("lgetxattr".to_string(), 192); - map.insert("linkat".to_string(), 265); - map.insert("link".to_string(), 86); - map.insert("listen".to_string(), 50); - map.insert("listxattr".to_string(), 194); - map.insert("llistxattr".to_string(), 195); - map.insert("lookup_dcookie".to_string(), 212); - map.insert("lremovexattr".to_string(), 198); - map.insert("lseek".to_string(), 8); - map.insert("lsetxattr".to_string(), 189); - map.insert("lstat".to_string(), 6); - map.insert("madvise".to_string(), 28); - map.insert("mbind".to_string(), 237); - map.insert("membarrier".to_string(), 324); - map.insert("memfd_create".to_string(), 319); - map.insert("migrate_pages".to_string(), 256); - map.insert("mincore".to_string(), 27); - map.insert("mkdirat".to_string(), 258); - map.insert("mkdir".to_string(), 83); - map.insert("mknodat".to_string(), 259); - map.insert("mknod".to_string(), 133); - map.insert("mlock2".to_string(), 325); - map.insert("mlockall".to_string(), 151); - map.insert("mlock".to_string(), 149); - map.insert("mmap".to_string(), 9); - map.insert("modify_ldt".to_string(), 154); - map.insert("mount".to_string(), 165); - map.insert("move_mount".to_string(), 429); - map.insert("move_pages".to_string(), 279); - map.insert("mprotect".to_string(), 10); - map.insert("mq_getsetattr".to_string(), 245); - map.insert("mq_notify".to_string(), 244); - map.insert("mq_open".to_string(), 240); - map.insert("mq_timedreceive".to_string(), 243); - map.insert("mq_timedsend".to_string(), 242); - map.insert("mq_unlink".to_string(), 241); - map.insert("mremap".to_string(), 25); - map.insert("msgctl".to_string(), 71); - map.insert("msgget".to_string(), 68); - map.insert("msgrcv".to_string(), 70); - map.insert("msgsnd".to_string(), 69); - map.insert("msync".to_string(), 26); - map.insert("munlockall".to_string(), 152); - map.insert("munlock".to_string(), 150); - map.insert("munmap".to_string(), 11); - map.insert("name_to_handle_at".to_string(), 303); - map.insert("nanosleep".to_string(), 35); - map.insert("newfstatat".to_string(), 262); - map.insert("nfsservctl".to_string(), 180); - map.insert("openat2".to_string(), 437); - map.insert("openat".to_string(), 257); - map.insert("open_by_handle_at".to_string(), 304); - map.insert("open".to_string(), 2); - map.insert("open_tree".to_string(), 428); - map.insert("pause".to_string(), 34); - map.insert("perf_event_open".to_string(), 298); - map.insert("personality".to_string(), 135); - map.insert("pidfd_getfd".to_string(), 438); - map.insert("pidfd_open".to_string(), 434); - map.insert("pidfd_send_signal".to_string(), 424); - map.insert("pipe2".to_string(), 293); - map.insert("pipe".to_string(), 22); - map.insert("pivot_root".to_string(), 155); - map.insert("pkey_alloc".to_string(), 330); - map.insert("pkey_free".to_string(), 331); - map.insert("pkey_mprotect".to_string(), 329); - map.insert("poll".to_string(), 7); - map.insert("ppoll".to_string(), 271); - map.insert("prctl".to_string(), 157); - map.insert("pread64".to_string(), 17); - map.insert("preadv2".to_string(), 327); - map.insert("preadv".to_string(), 295); - map.insert("prlimit64".to_string(), 302); - map.insert("process_madvise".to_string(), 440); - map.insert("process_vm_readv".to_string(), 310); - map.insert("process_vm_writev".to_string(), 311); - map.insert("pselect6".to_string(), 270); - map.insert("ptrace".to_string(), 101); - map.insert("putpmsg".to_string(), 182); - map.insert("pwrite64".to_string(), 18); - map.insert("pwritev2".to_string(), 328); - map.insert("pwritev".to_string(), 296); - map.insert("query_module".to_string(), 178); - map.insert("quotactl".to_string(), 179); - map.insert("readahead".to_string(), 187); - map.insert("readlinkat".to_string(), 267); - map.insert("readlink".to_string(), 89); - map.insert("read".to_string(), 0); - map.insert("readv".to_string(), 19); - map.insert("reboot".to_string(), 169); - map.insert("recvfrom".to_string(), 45); - map.insert("recvmmsg".to_string(), 299); - map.insert("recvmsg".to_string(), 47); - map.insert("remap_file_pages".to_string(), 216); - map.insert("removexattr".to_string(), 197); - map.insert("renameat2".to_string(), 316); - map.insert("renameat".to_string(), 264); - map.insert("rename".to_string(), 82); - map.insert("request_key".to_string(), 249); - map.insert("restart_syscall".to_string(), 219); - map.insert("rmdir".to_string(), 84); - map.insert("rseq".to_string(), 334); - map.insert("rt_sigaction".to_string(), 13); - map.insert("rt_sigpending".to_string(), 127); - map.insert("rt_sigprocmask".to_string(), 14); - map.insert("rt_sigqueueinfo".to_string(), 129); - map.insert("rt_sigreturn".to_string(), 15); - map.insert("rt_sigsuspend".to_string(), 130); - map.insert("rt_sigtimedwait".to_string(), 128); - map.insert("rt_tgsigqueueinfo".to_string(), 297); - map.insert("sched_getaffinity".to_string(), 204); - map.insert("sched_getattr".to_string(), 315); - map.insert("sched_getparam".to_string(), 143); - map.insert("sched_get_priority_max".to_string(), 146); - map.insert("sched_get_priority_min".to_string(), 147); - map.insert("sched_getscheduler".to_string(), 145); - map.insert("sched_rr_get_interval".to_string(), 148); - map.insert("sched_setaffinity".to_string(), 203); - map.insert("sched_setattr".to_string(), 314); - map.insert("sched_setparam".to_string(), 142); - map.insert("sched_setscheduler".to_string(), 144); - map.insert("sched_yield".to_string(), 24); - map.insert("seccomp".to_string(), 317); - map.insert("security".to_string(), 185); - map.insert("select".to_string(), 23); - map.insert("semctl".to_string(), 66); - map.insert("semget".to_string(), 64); - map.insert("semop".to_string(), 65); - map.insert("semtimedop".to_string(), 220); - map.insert("sendfile".to_string(), 40); - map.insert("sendmmsg".to_string(), 307); - map.insert("sendmsg".to_string(), 46); - map.insert("sendto".to_string(), 44); - map.insert("setdomainname".to_string(), 171); - map.insert("setfsgid".to_string(), 123); - map.insert("setfsuid".to_string(), 122); - map.insert("setgid".to_string(), 106); - map.insert("setgroups".to_string(), 116); - map.insert("sethostname".to_string(), 170); - map.insert("setitimer".to_string(), 38); - map.insert("set_mempolicy".to_string(), 238); - map.insert("setns".to_string(), 308); - map.insert("setpgid".to_string(), 109); - map.insert("setpriority".to_string(), 141); - map.insert("setregid".to_string(), 114); - map.insert("setresgid".to_string(), 119); - map.insert("setresuid".to_string(), 117); - map.insert("setreuid".to_string(), 113); - map.insert("setrlimit".to_string(), 160); - map.insert("set_robust_list".to_string(), 273); - map.insert("setsid".to_string(), 112); - map.insert("setsockopt".to_string(), 54); - map.insert("set_thread_area".to_string(), 205); - map.insert("set_tid_address".to_string(), 218); - map.insert("settimeofday".to_string(), 164); - map.insert("setuid".to_string(), 105); - map.insert("setxattr".to_string(), 188); - map.insert("shmat".to_string(), 30); - map.insert("shmctl".to_string(), 31); - map.insert("shmdt".to_string(), 67); - map.insert("shmget".to_string(), 29); - map.insert("shutdown".to_string(), 48); - map.insert("sigaltstack".to_string(), 131); - map.insert("signalfd4".to_string(), 289); - map.insert("signalfd".to_string(), 282); - map.insert("socketpair".to_string(), 53); - map.insert("socket".to_string(), 41); - map.insert("splice".to_string(), 275); - map.insert("statfs".to_string(), 137); - map.insert("stat".to_string(), 4); - map.insert("statx".to_string(), 332); - map.insert("swapoff".to_string(), 168); - map.insert("swapon".to_string(), 167); - map.insert("symlinkat".to_string(), 266); - map.insert("symlink".to_string(), 88); - map.insert("sync_file_range".to_string(), 277); - map.insert("syncfs".to_string(), 306); - map.insert("sync".to_string(), 162); - map.insert("_sysctl".to_string(), 156); - map.insert("sysfs".to_string(), 139); - map.insert("sysinfo".to_string(), 99); - map.insert("syslog".to_string(), 103); - map.insert("tee".to_string(), 276); - map.insert("tgkill".to_string(), 234); - map.insert("timer_create".to_string(), 222); - map.insert("timer_delete".to_string(), 226); - map.insert("timerfd_create".to_string(), 283); - map.insert("timerfd_gettime".to_string(), 287); - map.insert("timerfd_settime".to_string(), 286); - map.insert("timer_getoverrun".to_string(), 225); - map.insert("timer_gettime".to_string(), 224); - map.insert("timer_settime".to_string(), 223); - map.insert("times".to_string(), 100); - map.insert("time".to_string(), 201); - map.insert("tkill".to_string(), 200); - map.insert("truncate".to_string(), 76); - map.insert("tuxcall".to_string(), 184); - map.insert("umask".to_string(), 95); - map.insert("umount2".to_string(), 166); - map.insert("uname".to_string(), 63); - map.insert("unlinkat".to_string(), 263); - map.insert("unlink".to_string(), 87); - map.insert("unshare".to_string(), 272); - map.insert("uselib".to_string(), 134); - map.insert("userfaultfd".to_string(), 323); - map.insert("ustat".to_string(), 136); - map.insert("utimensat".to_string(), 280); - map.insert("utimes".to_string(), 235); - map.insert("utime".to_string(), 132); - map.insert("vfork".to_string(), 58); - map.insert("vhangup".to_string(), 153); - map.insert("vmsplice".to_string(), 278); - map.insert("vserver".to_string(), 236); - map.insert("wait4".to_string(), 61); - map.insert("waitid".to_string(), 247); - map.insert("write".to_string(), 1); - map.insert("writev".to_string(), 20); -} diff --git a/src/seccompiler/src/types.rs b/src/seccompiler/src/types.rs new file mode 100644 index 00000000000..2035f8b8ea4 --- /dev/null +++ b/src/seccompiler/src/types.rs @@ -0,0 +1,192 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::BTreeMap; +use std::ffi::CString; +use std::str::FromStr; + +use serde::*; + +// use libseccomp::{ScmpAction, ScmpArch, ScmpCompareOp}; +use crate::bindings::*; + +/// Comparison to perform when matching a condition. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum SeccompCmpOp { + Eq, + Ge, + Gt, + Le, + Lt, + MaskedEq(u64), + Ne, +} + +/// Seccomp argument value length. +#[derive(Clone, Debug, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum SeccompCmpArgLen { + /// Argument value length is 4 bytes. + Dword, + /// Argument value length is 8 bytes. + Qword, +} + +/// Condition that syscall must match in order to satisfy a rule. +#[derive(Debug, Deserialize)] +pub struct SeccompCondition { + pub index: u8, + pub op: SeccompCmpOp, + pub val: u64, + #[serde(rename = "type")] + pub val_len: SeccompCmpArgLen, +} + +impl SeccompCondition { + pub fn to_scmp_type(&self) -> scmp_arg_cmp { + match self.op { + SeccompCmpOp::Eq => { + // When using EQ libseccomp compares the whole 64 bits. In + // general this is not a problem, but for example we have + // observed musl `ioctl` to leave garbage in the upper bits of + // the `request` argument. There is a GH issue to allow 32bit + // comparisons (see + // https://github.com/seccomp/libseccomp/issues/383) but is not + // merged yet. Until that is available, do a masked comparison + // with the upper 32bits set to 0, so we will compare that `hi32 + // & 0x0 == 0`, which is always true. This costs one additional + // instruction, but will be likely be optimized away by the BPF + // JIT. + match self.val_len { + SeccompCmpArgLen::Dword => scmp_arg_cmp { + arg: self.index as u32, + op: scmp_compare::SCMP_CMP_MASKED_EQ, + datum_a: 0x00000000FFFFFFFF, + datum_b: self.val, + }, + SeccompCmpArgLen::Qword => scmp_arg_cmp { + arg: self.index as u32, + op: scmp_compare::SCMP_CMP_EQ, + datum_a: self.val, + datum_b: 0, + }, + } + } + SeccompCmpOp::Ge => scmp_arg_cmp { + arg: self.index as u32, + op: scmp_compare::SCMP_CMP_GE, + datum_a: self.val, + datum_b: 0, + }, + SeccompCmpOp::Gt => scmp_arg_cmp { + arg: self.index as u32, + op: scmp_compare::SCMP_CMP_GT, + datum_a: self.val, + datum_b: 0, + }, + SeccompCmpOp::Le => scmp_arg_cmp { + arg: self.index as u32, + op: scmp_compare::SCMP_CMP_LE, + datum_a: self.val, + datum_b: 0, + }, + SeccompCmpOp::Lt => scmp_arg_cmp { + arg: self.index as u32, + op: scmp_compare::SCMP_CMP_LT, + datum_a: self.val, + datum_b: 0, + }, + SeccompCmpOp::Ne => scmp_arg_cmp { + arg: self.index as u32, + op: scmp_compare::SCMP_CMP_NE, + datum_a: self.val, + datum_b: 0, + }, + + SeccompCmpOp::MaskedEq(m) => scmp_arg_cmp { + arg: self.index as u32, + op: scmp_compare::SCMP_CMP_MASKED_EQ, + datum_a: m, + datum_b: self.val, + }, + } + } +} + +/// Actions that `seccomp` can apply to process calling a syscall. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum SeccompAction { + Allow, + Errno(u16), + KillThread, + KillProcess, + Log, + Trace(u16), + Trap, +} + +impl SeccompAction { + pub fn to_scmp_type(&self) -> u32 { + match self { + SeccompAction::Allow => SCMP_ACT_ALLOW, + SeccompAction::Errno(e) => SCMP_ACT_ERRNO(*e), + SeccompAction::KillThread => SCMP_ACT_KILL_THREAD, + SeccompAction::KillProcess => SCMP_ACT_KILL_PROCESS, + SeccompAction::Log => SCMP_ACT_LOG, + SeccompAction::Trace(t) => SCMP_ACT_TRACE(*t), + SeccompAction::Trap => SCMP_ACT_TRAP, + } + } +} + +/// Rule that `seccomp` attempts to match for a syscall. +/// +/// If all conditions match then rule gets matched. +/// The action of the first rule that matches will be applied to the calling process. +/// If no rule matches the default action is applied. +#[derive(Debug, Deserialize)] +pub struct SyscallRule { + pub syscall: CString, + pub args: Option>, +} + +/// Filter containing rules assigned to syscall numbers. +#[derive(Debug, Deserialize)] +pub struct Filter { + pub default_action: SeccompAction, + pub filter_action: SeccompAction, + pub filter: Vec, +} + +/// Deserializable object that represents the Json filter file. +#[derive(Debug, Deserialize)] +pub struct BpfJson(pub BTreeMap); + +/// Supported target architectures. +#[derive(Debug)] +pub enum TargetArch { + X86_64, + Aarch64, +} + +impl TargetArch { + pub fn to_scmp_type(&self) -> u32 { + match self { + TargetArch::X86_64 => SCMP_ARCH_X86_64, + TargetArch::Aarch64 => SCMP_ARCH_AARCH64, + } + } +} + +impl FromStr for TargetArch { + type Err = String; + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "x86_64" => Ok(TargetArch::X86_64), + "aarch64" => Ok(TargetArch::Aarch64), + _ => Err(s.to_string()), + } + } +} diff --git a/src/snapshot-editor/Cargo.toml b/src/snapshot-editor/Cargo.toml index 7c341cbfd13..64c7dfe2ce4 100644 --- a/src/snapshot-editor/Cargo.toml +++ b/src/snapshot-editor/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "snapshot-editor" -version = "1.10.0-dev" +version = "1.12.0-dev" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" license = "Apache-2.0" [[bin]] @@ -10,19 +10,19 @@ name = "snapshot-editor" bench = false [dependencies] -clap = { version = "4.5.20", features = ["derive", "string"] } +clap = { version = "4.5.35", features = ["derive", "string"] } displaydoc = "0.2.5" fc_utils = { package = "utils", path = "../utils" } -libc = "0.2.161" +libc = "0.2.171" log-instrument = { path = "../log-instrument", optional = true } -semver = "1.0.23" -thiserror = "1.0.67" +semver = "1.0.26" +thiserror = "2.0.12" vmm = { path = "../vmm" } vmm-sys-util = "0.12.1" [target.'cfg(target_arch = "aarch64")'.dependencies] -clap-num = "1.0.2" +clap-num = "1.2.0" [features] tracing = ["log-instrument", "fc_utils/tracing", "vmm/tracing"] diff --git a/src/snapshot-editor/src/edit_vmstate.rs b/src/snapshot-editor/src/edit_vmstate.rs index 1d35cc19760..ba5b8370096 100644 --- a/src/snapshot-editor/src/edit_vmstate.rs +++ b/src/snapshot-editor/src/edit_vmstate.rs @@ -8,7 +8,7 @@ use clap_num::maybe_hex; use vmm::arch::aarch64::regs::Aarch64RegisterVec; use vmm::persist::MicrovmState; -use crate::utils::{open_vmstate, save_vmstate, UtilsError}; +use crate::utils::{UtilsError, open_vmstate, save_vmstate}; #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum EditVmStateError { @@ -99,7 +99,7 @@ mod tests { const KVM_REG_SIZE_U32: u64 = 0x20000000000000; use vmm::arch::aarch64::regs::Aarch64RegisterRef; - use vmm::vstate::vcpu::aarch64::VcpuState; + use vmm::arch::aarch64::vcpu::VcpuState; let vcpu_state = VcpuState { regs: { @@ -158,7 +158,7 @@ mod tests { const KVM_REG_SIZE_U32: u64 = 0x20000000000000; use vmm::arch::aarch64::regs::Aarch64RegisterRef; - use vmm::vstate::vcpu::aarch64::VcpuState; + use vmm::arch::aarch64::vcpu::VcpuState; let vcpu_state = VcpuState { regs: { diff --git a/src/snapshot-editor/src/main.rs b/src/snapshot-editor/src/main.rs index cc940b341f4..fbc4a2ab883 100644 --- a/src/snapshot-editor/src/main.rs +++ b/src/snapshot-editor/src/main.rs @@ -9,10 +9,10 @@ mod edit_vmstate; mod info; mod utils; -use edit_memory::{edit_memory_command, EditMemoryError, EditMemorySubCommand}; +use edit_memory::{EditMemoryError, EditMemorySubCommand, edit_memory_command}; #[cfg(target_arch = "aarch64")] -use edit_vmstate::{edit_vmstate_command, EditVmStateError, EditVmStateSubCommand}; -use info::{info_vmstate_command, InfoVmStateError, InfoVmStateSubCommand}; +use edit_vmstate::{EditVmStateError, EditVmStateSubCommand, edit_vmstate_command}; +use info::{InfoVmStateError, InfoVmStateSubCommand, info_vmstate_command}; #[derive(Debug, thiserror::Error, displaydoc::Display)] enum SnapEditorError { diff --git a/src/utils/Cargo.toml b/src/utils/Cargo.toml index cc0b979bfa6..9db225fd610 100644 --- a/src/utils/Cargo.toml +++ b/src/utils/Cargo.toml @@ -2,24 +2,17 @@ name = "utils" version = "0.1.0" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" license = "Apache-2.0" [lib] bench = false [dependencies] -derive_more = { version = "1.0.0", default-features = false, features = ["from"] } displaydoc = "0.2.5" -libc = "0.2.161" +libc = "0.2.171" log-instrument = { path = "../log-instrument", optional = true } -serde = { version = "1.0.214", features = ["derive"] } -thiserror = "1.0.67" -vm-memory = { version = "0.16.0", features = ["backend-mmap", "backend-bitmap"] } -vmm-sys-util = "0.12.1" - -[dev-dependencies] -serde_json = "1.0.132" +thiserror = "2.0.12" [features] tracing = ["log-instrument"] diff --git a/src/utils/src/arg_parser.rs b/src/utils/src/arg_parser.rs index 47dfe3a88a4..e76d38b7609 100644 --- a/src/utils/src/arg_parser.rs +++ b/src/utils/src/arg_parser.rs @@ -438,7 +438,7 @@ impl<'a> Arguments<'a> { _ => { return Err(UtilsArgParserError::UnexpectedArgument( argument.name.to_string(), - )) + )); } } } else { diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index c9a032edb95..f0bf19edfc8 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -2,51 +2,48 @@ name = "vmm" version = "0.1.0" authors = ["Amazon Firecracker team "] -edition = "2021" +edition = "2024" license = "Apache-2.0" [lib] bench = false [dependencies] -acpi_tables = { path = "../acpi-tables" } +acpi_tables = { path = "../acpi-tables" } aes-gcm = { version = "0.10.1", default-features = false, features = ["aes"] } arrayvec = { version = "0.7.6", optional = true } -aws-lc-rs = { version = "1.10.0", features = ["bindgen"] } +aws-lc-rs = { version = "1.13.0", features = ["bindgen"] } base64 = "0.22.1" -bincode = "1.2.1" -bitflags = "2.6.0" +bincode = { version = "2.0.1", features = ["serde"] } +bitflags = "2.9.0" crc64 = "2.0.0" -derive_more = { version = "1.0.0", default-features = false, features = ["from", "display"] } +derive_more = { version = "2.0.1", default-features = false, features = ["from", "display"] } displaydoc = "0.2.5" event-manager = "0.4.0" -gdbstub = { version = "0.7.3", optional = true } +gdbstub = { version = "0.7.5", optional = true } gdbstub_arch = { version = "0.3.1", optional = true } -kvm-bindings = { version = "0.10.0", features = ["fam-wrappers", "serde"] } -kvm-ioctls = "0.19.0" -lazy_static = "1.5.0" -libc = "0.2.161" +kvm-bindings = { version = "0.11.1", features = ["fam-wrappers", "serde"] } +kvm-ioctls = "0.21.0" +libc = "0.2.171" linux-loader = "0.13.0" -log = { version = "0.4.22", features = ["std", "serde"] } +log = { version = "0.4.27", features = ["std", "serde"] } log-instrument = { path = "../log-instrument", optional = true } memfd = "0.6.3" micro_http = { git = "https://github.com/firecracker-microvm/micro-http" } - -seccompiler = { path = "../seccompiler" } -semver = { version = "1.0.23", features = ["serde"] } -serde = { version = "1.0.214", features = ["derive", "rc"] } -serde_json = "1.0.132" +semver = { version = "1.0.26", features = ["serde"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } +serde_json = "1.0.140" slab = "0.4.7" -thiserror = "1.0.67" +thiserror = "2.0.12" timerfd = "1.5.0" userfaultfd = "0.8.1" utils = { path = "../utils" } vhost = { version = "0.13.0", features = ["vhost-user-frontend"] } vm-allocator = "0.1.0" -vm-memory = { version = "0.16.0", features = ["backend-mmap", "backend-bitmap"] } +vm-memory = { version = "0.16.1", features = ["backend-mmap", "backend-bitmap"] } vm-superio = "0.8.0" vmm-sys-util = { version = "0.12.1", features = ["with-serde"] } -zerocopy = { version = "0.8.8" } +zerocopy = { version = "0.8.24" } [target.'cfg(target_arch = "aarch64")'.dependencies] vm-fdt = "0.3.0" @@ -54,8 +51,8 @@ vm-fdt = "0.3.0" [dev-dependencies] criterion = { version = "0.5.0", default-features = false } device_tree = "1.1.0" -itertools = "0.13.0" -proptest = { version = "1.5.0", default-features = false, features = ["std"] } +itertools = "0.14.0" +proptest = { version = "1.6.0", default-features = false, features = ["std"] } [features] default = [] diff --git a/src/vmm/benches/block_request.rs b/src/vmm/benches/block_request.rs index a616a48b5bf..1ccf3e7c3b6 100644 --- a/src/vmm/benches/block_request.rs +++ b/src/vmm/benches/block_request.rs @@ -6,7 +6,7 @@ // * `Queue.add_used` // * `DescriptorChain.next_descriptor` -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; use vm_memory::GuestAddress; use vmm::devices::virtio::block::virtio::test_utils::RequestDescriptorChain; use vmm::devices::virtio::block::virtio::{Request, RequestHeader, VIRTIO_BLK_T_IN}; diff --git a/src/vmm/benches/cpu_templates.rs b/src/vmm/benches/cpu_templates.rs index 75060a58675..e31ad7261aa 100644 --- a/src/vmm/benches/cpu_templates.rs +++ b/src/vmm/benches/cpu_templates.rs @@ -7,9 +7,9 @@ use std::mem::size_of_val; -use criterion::{criterion_group, criterion_main, Criterion}; -use vmm::cpu_config::templates::test_utils::{build_test_template, TEST_TEMPLATE_JSON}; +use criterion::{Criterion, criterion_group, criterion_main}; use vmm::cpu_config::templates::CustomCpuTemplate; +use vmm::cpu_config::templates::test_utils::{TEST_TEMPLATE_JSON, build_test_template}; #[inline] pub fn bench_serialize_cpu_template(cpu_template: &CustomCpuTemplate) { diff --git a/src/vmm/benches/memory_access.rs b/src/vmm/benches/memory_access.rs index 0da710492f7..fe4f138db2d 100644 --- a/src/vmm/benches/memory_access.rs +++ b/src/vmm/benches/memory_access.rs @@ -1,10 +1,9 @@ // Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; -use vm_memory::GuestMemory; +use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; use vmm::resources::VmResources; -use vmm::vmm_config::machine_config::{HugePageConfig, VmConfig}; +use vmm::vmm_config::machine_config::{HugePageConfig, MachineConfig}; fn bench_single_page_fault(c: &mut Criterion, configuration: VmResources) { c.bench_function("page_fault", |b| { @@ -14,7 +13,7 @@ fn bench_single_page_fault(c: &mut Criterion, configuration: VmResources) { // Get a pointer to the first memory region (cannot do `.get_slice(GuestAddress(0), // 1)`, because on ARM64 guest memory does not start at physical // address 0). - let ptr = memory.iter().next().unwrap().as_ptr(); + let ptr = memory.first().unwrap().as_ptr(); // fine to return both here, because ptr is not a reference into `memory` (e.g. no // self-referential structs are happening here) @@ -33,7 +32,7 @@ pub fn bench_4k_page_fault(c: &mut Criterion) { bench_single_page_fault( c, VmResources { - vm_config: VmConfig { + machine_config: MachineConfig { vcpu_count: 1, mem_size_mib: 2, ..Default::default() @@ -47,7 +46,7 @@ pub fn bench_2m_page_fault(c: &mut Criterion) { bench_single_page_fault( c, VmResources { - vm_config: VmConfig { + machine_config: MachineConfig { vcpu_count: 1, mem_size_mib: 2, huge_pages: HugePageConfig::Hugetlbfs2M, diff --git a/src/vmm/benches/queue.rs b/src/vmm/benches/queue.rs index faff57b2ebf..b5536fa7ef1 100644 --- a/src/vmm/benches/queue.rs +++ b/src/vmm/benches/queue.rs @@ -8,7 +8,7 @@ use std::num::Wrapping; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; use vm_memory::GuestAddress; use vmm::devices::virtio::queue::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE}; use vmm::devices::virtio::test_utils::VirtQueue; @@ -88,7 +88,7 @@ pub fn queue_benchmark(c: &mut Criterion) { for i in 0_u16..16_u16 { let index = std::hint::black_box(i); let len = std::hint::black_box(i + 1); - _ = queue.add_used(index as u16, len as u32); + _ = queue.add_used(index, len as u32); } }) }); @@ -100,7 +100,7 @@ pub fn queue_benchmark(c: &mut Criterion) { for i in 0_u16..256_u16 { let index = std::hint::black_box(i); let len = std::hint::black_box(i + 1); - _ = queue.add_used(index as u16, len as u32); + _ = queue.add_used(index, len as u32); } }) }); diff --git a/src/vmm/src/acpi/mod.rs b/src/vmm/src/acpi/mod.rs index 247c832e665..0b5c5edcbde 100644 --- a/src/vmm/src/acpi/mod.rs +++ b/src/vmm/src/acpi/mod.rs @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use acpi_tables::fadt::{FADT_F_HW_REDUCED_ACPI, FADT_F_PWR_BUTTON, FADT_F_SLP_BUTTON}; -use acpi_tables::{aml, Aml, Dsdt, Fadt, Madt, Rsdp, Sdt, Xsdt}; +use acpi_tables::{Aml, Dsdt, Fadt, Madt, Rsdp, Sdt, Xsdt, aml}; use log::{debug, error}; use vm_allocator::AllocPolicy; +use crate::Vcpu; use crate::acpi::x86_64::{ apic_addr, rsdp_addr, setup_arch_dsdt, setup_arch_fadt, setup_interrupt_controllers, }; @@ -13,7 +14,6 @@ use crate::device_manager::acpi::ACPIDeviceManager; use crate::device_manager::mmio::MMIODeviceManager; use crate::device_manager::resources::ResourceAllocator; use crate::vstate::memory::{GuestAddress, GuestMemoryMmap}; -use crate::Vcpu; mod x86_64; @@ -48,7 +48,7 @@ struct AcpiTableWriter<'a> { resource_allocator: &'a mut ResourceAllocator, } -impl<'a> AcpiTableWriter<'a> { +impl AcpiTableWriter<'_> { /// Write a table in guest memory /// /// This will allocate enough space inside guest memory and write the table in the allocated @@ -105,7 +105,7 @@ impl<'a> AcpiTableWriter<'a> { fadt.set_hypervisor_vendor_id(HYPERVISOR_VENDOR_ID); fadt.set_x_dsdt(dsdt_addr); fadt.set_flags( - 1 << FADT_F_HW_REDUCED_ACPI | 1 << FADT_F_PWR_BUTTON | 1 << FADT_F_SLP_BUTTON, + (1 << FADT_F_HW_REDUCED_ACPI) | (1 << FADT_F_PWR_BUTTON) | (1 << FADT_F_SLP_BUTTON), ); setup_arch_fadt(&mut fadt); self.write_acpi_table(&mut fadt) @@ -181,14 +181,16 @@ pub(crate) fn create_acpi_tables( } #[cfg(test)] -pub mod tests { +mod tests { use acpi_tables::Sdt; use vm_memory::Bytes; use crate::acpi::{AcpiError, AcpiTableWriter}; use crate::arch::x86_64::layout::{SYSTEM_MEM_SIZE, SYSTEM_MEM_START}; use crate::builder::tests::default_vmm; - use crate::test_utils::arch_mem; + use crate::device_manager::resources::ResourceAllocator; + use crate::utils::u64_to_usize; + use crate::vstate::vm::tests::setup_vm_with_memory; struct MockSdt(Vec); @@ -215,7 +217,7 @@ pub mod tests { // A mocke Vmm object with 128MBs of memory let mut vmm = default_vmm(); let mut writer = AcpiTableWriter { - mem: &vmm.guest_memory, + mem: vmm.vm.guest_memory(), resource_allocator: &mut vmm.resource_allocator, }; @@ -263,15 +265,10 @@ pub mod tests { // change in the future. #[test] fn test_write_acpi_table_small_memory() { - let mut vmm = default_vmm(); - vmm.guest_memory = arch_mem( - (SYSTEM_MEM_START + SYSTEM_MEM_SIZE - 4096) - .try_into() - .unwrap(), - ); + let (_, vm) = setup_vm_with_memory(u64_to_usize(SYSTEM_MEM_START + SYSTEM_MEM_SIZE - 4096)); let mut writer = AcpiTableWriter { - mem: &vmm.guest_memory, - resource_allocator: &mut vmm.resource_allocator, + mem: vm.guest_memory(), + resource_allocator: &mut ResourceAllocator::new().unwrap(), }; let mut sdt = MockSdt(vec![0; usize::try_from(SYSTEM_MEM_SIZE).unwrap()]); diff --git a/src/vmm/src/acpi/x86_64.rs b/src/vmm/src/acpi/x86_64.rs index 7d2e9f0364a..de850a9989f 100644 --- a/src/vmm/src/acpi/x86_64.rs +++ b/src/vmm/src/acpi/x86_64.rs @@ -8,7 +8,7 @@ use acpi_tables::fadt::{ IAPC_BOOT_ARG_FLAGS_VGA_NOT_PRESENT, }; use acpi_tables::madt::{IoAPIC, LocalAPIC}; -use acpi_tables::{aml, Fadt}; +use acpi_tables::{Fadt, aml}; use vm_memory::GuestAddress; use zerocopy::IntoBytes; @@ -34,9 +34,9 @@ pub(crate) fn setup_arch_fadt(fadt: &mut Fadt) { // More info here: // https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html?highlight=0a06#ia-pc-boot-architecture-flags fadt.setup_iapc_flags( - 1 << IAPC_BOOT_ARG_FLAGS_VGA_NOT_PRESENT - | 1 << IAPC_BOOT_ARG_FLAGS_PCI_ASPM - | 1 << IAPC_BOOT_ARG_FLAGS_MSI_NOT_PRESENT, + (1 << IAPC_BOOT_ARG_FLAGS_VGA_NOT_PRESENT) + | (1 << IAPC_BOOT_ARG_FLAGS_PCI_ASPM) + | (1 << IAPC_BOOT_ARG_FLAGS_MSI_NOT_PRESENT), ); } diff --git a/src/vmm/src/arch/aarch64/cache_info.rs b/src/vmm/src/arch/aarch64/cache_info.rs index cd61cabeb02..8f8611fe440 100644 --- a/src/vmm/src/arch/aarch64/cache_info.rs +++ b/src/vmm/src/arch/aarch64/cache_info.rs @@ -45,6 +45,7 @@ pub(crate) struct CacheEntry { } #[derive(Debug)] +#[cfg_attr(test, allow(dead_code))] struct HostCacheStore { cache_dir: PathBuf, } @@ -206,6 +207,7 @@ impl CacheType { } } +#[cfg_attr(test, allow(unused))] fn readln_special>(file_path: &T) -> Result { let line = fs::read_to_string(file_path)?; Ok(line.trim_end().to_string()) @@ -318,7 +320,7 @@ mod tests { use super::*; use crate::arch::aarch64::cache_info::{ - read_cache_config, CacheEngine, CacheEntry, CacheStore, + CacheEngine, CacheEntry, CacheStore, read_cache_config, }; #[derive(Debug)] diff --git a/src/vmm/src/arch/aarch64/fdt.rs b/src/vmm/src/arch/aarch64/fdt.rs index ff8c561910a..61200cb2148 100644 --- a/src/vmm/src/arch/aarch64/fdt.rs +++ b/src/vmm/src/arch/aarch64/fdt.rs @@ -12,10 +12,12 @@ use std::fmt::Debug; use vm_fdt::{Error as VmFdtError, FdtWriter, FdtWriterNode}; use vm_memory::GuestMemoryError; -use super::super::{DeviceType, InitrdConfig}; -use super::cache_info::{read_cache_config, CacheEntry}; +use super::super::DeviceType; +use super::cache_info::{CacheEntry, read_cache_config}; use super::gic::GICDevice; -use crate::devices::acpi::vmgenid::{VmGenId, VMGENID_MEM_SIZE}; +use crate::device_manager::mmio::MMIODeviceInfo; +use crate::devices::acpi::vmgenid::{VMGENID_MEM_SIZE, VmGenId}; +use crate::initrd::InitrdConfig; use crate::vstate::memory::{Address, GuestMemory, GuestMemoryMmap}; // This is a value for uniquely identifying the FDT node declaring the interrupt controller. @@ -42,16 +44,6 @@ const GIC_FDT_IRQ_TYPE_PPI: u32 = 1; const IRQ_TYPE_EDGE_RISING: u32 = 1; const IRQ_TYPE_LEVEL_HI: u32 = 4; -/// Trait for devices to be added to the Flattened Device Tree. -pub trait DeviceInfoForFDT { - /// Returns the address where this device will be loaded. - fn addr(&self) -> u64; - /// Returns the associated interrupt for this device. - fn irq(&self) -> u32; - /// Returns the amount of memory that needs to be reserved for this device. - fn length(&self) -> u64; -} - /// Errors thrown while configuring the Flattened Device Tree for aarch64. #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum FdtError { @@ -64,11 +56,11 @@ pub enum FdtError { } /// Creates the flattened device tree for this aarch64 microVM. -pub fn create_fdt( +pub fn create_fdt( guest_mem: &GuestMemoryMmap, vcpu_mpidr: Vec, cmdline: CString, - device_info: &HashMap<(DeviceType, String), T>, + device_info: &HashMap<(DeviceType, String), MMIODeviceInfo>, gic_device: &GICDevice, vmgenid: &Option, initrd: &Option, @@ -361,17 +353,18 @@ fn create_psci_node(fdt: &mut FdtWriter) -> Result<(), FdtError> { Ok(()) } -fn create_virtio_node( - fdt: &mut FdtWriter, - dev_info: &T, -) -> Result<(), FdtError> { - let virtio_mmio = fdt.begin_node(&format!("virtio_mmio@{:x}", dev_info.addr()))?; +fn create_virtio_node(fdt: &mut FdtWriter, dev_info: &MMIODeviceInfo) -> Result<(), FdtError> { + let virtio_mmio = fdt.begin_node(&format!("virtio_mmio@{:x}", dev_info.addr))?; fdt.property_string("compatible", "virtio,mmio")?; - fdt.property_array_u64("reg", &[dev_info.addr(), dev_info.length()])?; + fdt.property_array_u64("reg", &[dev_info.addr, dev_info.len])?; fdt.property_array_u32( "interrupts", - &[GIC_FDT_IRQ_TYPE_SPI, dev_info.irq(), IRQ_TYPE_EDGE_RISING], + &[ + GIC_FDT_IRQ_TYPE_SPI, + dev_info.irq.unwrap().into(), + IRQ_TYPE_EDGE_RISING, + ], )?; fdt.property_u32("interrupt-parent", GIC_PHANDLE)?; fdt.end_node(virtio_mmio)?; @@ -379,38 +372,36 @@ fn create_virtio_node( Ok(()) } -fn create_serial_node( - fdt: &mut FdtWriter, - dev_info: &T, -) -> Result<(), FdtError> { - let serial = fdt.begin_node(&format!("uart@{:x}", dev_info.addr()))?; +fn create_serial_node(fdt: &mut FdtWriter, dev_info: &MMIODeviceInfo) -> Result<(), FdtError> { + let serial = fdt.begin_node(&format!("uart@{:x}", dev_info.addr))?; fdt.property_string("compatible", "ns16550a")?; - fdt.property_array_u64("reg", &[dev_info.addr(), dev_info.length()])?; + fdt.property_array_u64("reg", &[dev_info.addr, dev_info.len])?; fdt.property_u32("clocks", CLOCK_PHANDLE)?; fdt.property_string("clock-names", "apb_pclk")?; fdt.property_array_u32( "interrupts", - &[GIC_FDT_IRQ_TYPE_SPI, dev_info.irq(), IRQ_TYPE_EDGE_RISING], + &[ + GIC_FDT_IRQ_TYPE_SPI, + dev_info.irq.unwrap().into(), + IRQ_TYPE_EDGE_RISING, + ], )?; fdt.end_node(serial)?; Ok(()) } -fn create_rtc_node( - fdt: &mut FdtWriter, - dev_info: &T, -) -> Result<(), FdtError> { +fn create_rtc_node(fdt: &mut FdtWriter, dev_info: &MMIODeviceInfo) -> Result<(), FdtError> { // Driver requirements: // https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/rtc/arm,pl031.yaml // We do not offer the `interrupt` property because the device // does not implement interrupt support. let compatible = b"arm,pl031\0arm,primecell\0"; - let rtc = fdt.begin_node(&format!("rtc@{:x}", dev_info.addr()))?; + let rtc = fdt.begin_node(&format!("rtc@{:x}", dev_info.addr))?; fdt.property("compatible", compatible)?; - fdt.property_array_u64("reg", &[dev_info.addr(), dev_info.length()])?; + fdt.property_array_u64("reg", &[dev_info.addr, dev_info.len])?; fdt.property_u32("clocks", CLOCK_PHANDLE)?; fdt.property_string("clock-names", "apb_pclk")?; fdt.end_node(rtc)?; @@ -418,12 +409,12 @@ fn create_rtc_node( Ok(()) } -fn create_devices_node( +fn create_devices_node( fdt: &mut FdtWriter, - dev_info: &HashMap<(DeviceType, String), T, S>, + dev_info: &HashMap<(DeviceType, String), MMIODeviceInfo>, ) -> Result<(), FdtError> { // Create one temp Vec to store all virtio devices - let mut ordered_virtio_device: Vec<&T> = Vec::new(); + let mut ordered_virtio_device: Vec<&MMIODeviceInfo> = Vec::new(); for ((device_type, _device_id), info) in dev_info { match device_type { @@ -437,7 +428,7 @@ fn create_devices_node 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: u32) { @@ -493,17 +468,26 @@ mod tests { let dev_info: HashMap<(DeviceType, std::string::String), MMIODeviceInfo> = [ ( (DeviceType::Serial, DeviceType::Serial.to_string()), - MMIODeviceInfo { addr: 0x00, irq: 1 }, + MMIODeviceInfo { + addr: 0x00, + irq: NonZeroU32::new(1), + len: LEN, + }, ), ( (DeviceType::Virtio(1), "virtio".to_string()), - MMIODeviceInfo { addr: LEN, irq: 2 }, + MMIODeviceInfo { + addr: LEN, + irq: NonZeroU32::new(2), + len: LEN, + }, ), ( (DeviceType::Rtc, "rtc".to_string()), MMIODeviceInfo { addr: 2 * LEN, - irq: 3, + irq: NonZeroU32::new(3), + len: LEN, }, ), ] diff --git a/src/vmm/src/arch/aarch64/gic/gicv2/regs/dist_regs.rs b/src/vmm/src/arch/aarch64/gic/gicv2/regs/dist_regs.rs index 3ec73490038..21a404b302b 100644 --- a/src/vmm/src/arch/aarch64/gic/gicv2/regs/dist_regs.rs +++ b/src/vmm/src/arch/aarch64/gic/gicv2/regs/dist_regs.rs @@ -6,8 +6,8 @@ use std::ops::Range; use kvm_bindings::KVM_DEV_ARM_VGIC_GRP_DIST_REGS; use kvm_ioctls::DeviceFd; -use crate::arch::aarch64::gic::regs::{GicRegState, MmioReg, SimpleReg, VgicRegEngine}; use crate::arch::aarch64::gic::GicError; +use crate::arch::aarch64::gic::regs::{GicRegState, MmioReg, SimpleReg, VgicRegEngine}; use crate::arch::{IRQ_BASE, IRQ_MAX}; // Distributor registers as detailed at page 75 from @@ -133,7 +133,7 @@ mod tests { use kvm_ioctls::Kvm; use super::*; - use crate::arch::aarch64::gic::{create_gic, GICVersion, GicError}; + use crate::arch::aarch64::gic::{GICVersion, GicError, create_gic}; #[test] fn test_access_dist_regs() { @@ -162,5 +162,8 @@ mod tests { format!("{:?}", res.unwrap_err()), "DeviceAttribute(Error(9), false, 1)" ); + + // dropping gic_fd would double close the gic fd, so leak it + std::mem::forget(gic_fd); } } diff --git a/src/vmm/src/arch/aarch64/gic/gicv2/regs/icc_regs.rs b/src/vmm/src/arch/aarch64/gic/gicv2/regs/icc_regs.rs index aea6cb722b6..d50b74a5fb4 100644 --- a/src/vmm/src/arch/aarch64/gic/gicv2/regs/icc_regs.rs +++ b/src/vmm/src/arch/aarch64/gic/gicv2/regs/icc_regs.rs @@ -4,8 +4,8 @@ use kvm_bindings::*; use kvm_ioctls::DeviceFd; -use crate::arch::aarch64::gic::regs::{SimpleReg, VgicRegEngine, VgicSysRegsState}; use crate::arch::aarch64::gic::GicError; +use crate::arch::aarch64::gic::regs::{SimpleReg, VgicRegEngine, VgicSysRegsState}; // CPU interface registers as detailed at page 76 from // https://developer.arm.com/documentation/ihi0048/latest/. @@ -86,7 +86,7 @@ mod tests { use kvm_ioctls::Kvm; use super::*; - use crate::arch::aarch64::gic::{create_gic, GICVersion, GicError}; + use crate::arch::aarch64::gic::{GICVersion, GicError, create_gic}; #[test] fn test_access_icc_regs() { @@ -120,5 +120,8 @@ mod tests { format!("{:?}", res.unwrap_err()), "DeviceAttribute(Error(9), false, 2)" ); + + // dropping gic_fd would double close the gic fd, so leak it + std::mem::forget(gic_fd); } } diff --git a/src/vmm/src/arch/aarch64/gic/gicv2/regs/mod.rs b/src/vmm/src/arch/aarch64/gic/gicv2/regs/mod.rs index a0c0e2c8fac..8bb26ce2bcd 100644 --- a/src/vmm/src/arch/aarch64/gic/gicv2/regs/mod.rs +++ b/src/vmm/src/arch/aarch64/gic/gicv2/regs/mod.rs @@ -6,8 +6,8 @@ mod icc_regs; use kvm_ioctls::DeviceFd; -use crate::arch::aarch64::gic::regs::{GicState, GicVcpuState}; use crate::arch::aarch64::gic::GicError; +use crate::arch::aarch64::gic::regs::{GicState, GicVcpuState}; /// Save the state of the GIC device. pub fn save_state(fd: &DeviceFd, mpidrs: &[u64]) -> Result { @@ -46,7 +46,7 @@ mod tests { use kvm_ioctls::Kvm; use super::*; - use crate::arch::aarch64::gic::{create_gic, GICVersion}; + use crate::arch::aarch64::gic::{GICVersion, create_gic}; #[test] fn test_vm_save_restore_state() { diff --git a/src/vmm/src/arch/aarch64/gic/gicv3/mod.rs b/src/vmm/src/arch/aarch64/gic/gicv3/mod.rs index 21f9b94218d..558b47ab065 100644 --- a/src/vmm/src/arch/aarch64/gic/gicv3/mod.rs +++ b/src/vmm/src/arch/aarch64/gic/gicv3/mod.rs @@ -202,7 +202,7 @@ mod tests { use kvm_ioctls::Kvm; use super::*; - use crate::arch::aarch64::gic::{create_gic, GICVersion}; + use crate::arch::aarch64::gic::{GICVersion, create_gic}; #[test] fn test_save_pending_tables() { @@ -220,5 +220,8 @@ mod tests { format!("{:?}", res.unwrap_err()), "DeviceAttribute(Error(9), true, 4)" ); + + // dropping gic_fd would double close the gic fd, so leak it + std::mem::forget(gic); } } diff --git a/src/vmm/src/arch/aarch64/gic/gicv3/regs/dist_regs.rs b/src/vmm/src/arch/aarch64/gic/gicv3/regs/dist_regs.rs index 6e0cc8aac23..96c617dcc17 100644 --- a/src/vmm/src/arch/aarch64/gic/gicv3/regs/dist_regs.rs +++ b/src/vmm/src/arch/aarch64/gic/gicv3/regs/dist_regs.rs @@ -6,8 +6,8 @@ use std::ops::Range; use kvm_bindings::KVM_DEV_ARM_VGIC_GRP_DIST_REGS; use kvm_ioctls::DeviceFd; -use crate::arch::aarch64::gic::regs::{GicRegState, MmioReg, SimpleReg, VgicRegEngine}; use crate::arch::aarch64::gic::GicError; +use crate::arch::aarch64::gic::regs::{GicRegState, MmioReg, SimpleReg, VgicRegEngine}; use crate::arch::{IRQ_BASE, IRQ_MAX}; // Distributor registers as detailed at page 456 from @@ -134,7 +134,7 @@ mod tests { use kvm_ioctls::Kvm; use super::*; - use crate::arch::aarch64::gic::{create_gic, GICVersion}; + use crate::arch::aarch64::gic::{GICVersion, create_gic}; #[test] fn test_access_dist_regs() { @@ -159,6 +159,9 @@ mod tests { format!("{:?}", res.unwrap_err()), "DeviceAttribute(Error(9), false, 1)" ); + + // dropping gic_fd would double close the gic fd, so leak it + std::mem::forget(gic_fd); } #[test] diff --git a/src/vmm/src/arch/aarch64/gic/gicv3/regs/icc_regs.rs b/src/vmm/src/arch/aarch64/gic/gicv3/regs/icc_regs.rs index e455e76ba43..f79430e9a13 100644 --- a/src/vmm/src/arch/aarch64/gic/gicv3/regs/icc_regs.rs +++ b/src/vmm/src/arch/aarch64/gic/gicv3/regs/icc_regs.rs @@ -4,8 +4,8 @@ use kvm_bindings::*; use kvm_ioctls::DeviceFd; -use crate::arch::aarch64::gic::regs::{SimpleReg, VgicRegEngine, VgicSysRegsState}; use crate::arch::aarch64::gic::GicError; +use crate::arch::aarch64::gic::regs::{SimpleReg, VgicRegEngine, VgicSysRegsState}; const ICC_CTLR_EL1_PRIBITS_SHIFT: u64 = 8; const ICC_CTLR_EL1_PRIBITS_MASK: u64 = 7 << ICC_CTLR_EL1_PRIBITS_SHIFT; @@ -170,7 +170,7 @@ mod tests { use kvm_ioctls::Kvm; use super::*; - use crate::arch::aarch64::gic::{create_gic, GICVersion}; + use crate::arch::aarch64::gic::{GICVersion, create_gic}; #[test] fn test_access_icc_regs() { @@ -206,6 +206,9 @@ mod tests { format!("{:?}", res.unwrap_err()), "DeviceAttribute(Error(9), false, 6)" ); + + // dropping gic_fd would double close the gic fd, so leak it + std::mem::forget(gic_fd); } #[test] diff --git a/src/vmm/src/arch/aarch64/gic/gicv3/regs/mod.rs b/src/vmm/src/arch/aarch64/gic/gicv3/regs/mod.rs index 31261f647fc..0531766dc54 100644 --- a/src/vmm/src/arch/aarch64/gic/gicv3/regs/mod.rs +++ b/src/vmm/src/arch/aarch64/gic/gicv3/regs/mod.rs @@ -7,8 +7,8 @@ mod redist_regs; use kvm_ioctls::DeviceFd; -use crate::arch::aarch64::gic::regs::{GicState, GicVcpuState}; use crate::arch::aarch64::gic::GicError; +use crate::arch::aarch64::gic::regs::{GicState, GicVcpuState}; /// Save the state of the GIC device. pub fn save_state(fd: &DeviceFd, mpidrs: &[u64]) -> Result { @@ -51,7 +51,7 @@ mod tests { use kvm_ioctls::Kvm; use super::*; - use crate::arch::aarch64::gic::{create_gic, GICVersion}; + use crate::arch::aarch64::gic::{GICVersion, create_gic}; #[test] fn test_vm_save_restore_state() { diff --git a/src/vmm/src/arch/aarch64/gic/gicv3/regs/redist_regs.rs b/src/vmm/src/arch/aarch64/gic/gicv3/regs/redist_regs.rs index 1909d9b453b..4d1ba3292c1 100644 --- a/src/vmm/src/arch/aarch64/gic/gicv3/regs/redist_regs.rs +++ b/src/vmm/src/arch/aarch64/gic/gicv3/regs/redist_regs.rs @@ -4,8 +4,8 @@ use kvm_bindings::*; use kvm_ioctls::DeviceFd; -use crate::arch::aarch64::gic::regs::{GicRegState, SimpleReg, VgicRegEngine}; use crate::arch::aarch64::gic::GicError; +use crate::arch::aarch64::gic::regs::{GicRegState, SimpleReg, VgicRegEngine}; // Relevant PPI redistributor registers that we want to save/restore. const GICR_CTLR: SimpleReg = SimpleReg::new(0x0000, 4); @@ -91,7 +91,7 @@ mod tests { use kvm_ioctls::Kvm; use super::*; - use crate::arch::aarch64::gic::{create_gic, GICVersion}; + use crate::arch::aarch64::gic::{GICVersion, create_gic}; #[test] fn test_access_redist_regs() { @@ -120,5 +120,8 @@ mod tests { format!("{:?}", res.unwrap_err()), "DeviceAttribute(Error(9), false, 5)" ); + + // dropping gic_fd would double close the gic fd, so leak it + std::mem::forget(gic_fd); } } diff --git a/src/vmm/src/arch/aarch64/kvm.rs b/src/vmm/src/arch/aarch64/kvm.rs new file mode 100644 index 00000000000..ed66d722970 --- /dev/null +++ b/src/vmm/src/arch/aarch64/kvm.rs @@ -0,0 +1,60 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use std::convert::Infallible; + +use kvm_ioctls::Kvm as KvmFd; + +use crate::cpu_config::templates::KvmCapability; + +/// ['Kvm'] initialization can't fail for Aarch64 +pub type KvmArchError = Infallible; + +/// Optional capabilities. +#[derive(Debug, Default)] +pub struct OptionalCapabilities { + /// KVM_CAP_COUNTER_OFFSET + pub counter_offset: bool, +} + +/// Struct with kvm fd and kvm associated parameters. +#[derive(Debug)] +pub struct Kvm { + /// KVM fd. + pub fd: KvmFd, + /// Additional capabilities that were specified in cpu template. + pub kvm_cap_modifiers: Vec, +} + +impl Kvm { + pub(crate) const DEFAULT_CAPABILITIES: [u32; 7] = [ + kvm_bindings::KVM_CAP_IOEVENTFD, + kvm_bindings::KVM_CAP_IRQFD, + kvm_bindings::KVM_CAP_USER_MEMORY, + kvm_bindings::KVM_CAP_ARM_PSCI_0_2, + kvm_bindings::KVM_CAP_DEVICE_CTRL, + kvm_bindings::KVM_CAP_MP_STATE, + kvm_bindings::KVM_CAP_ONE_REG, + ]; + + /// Initialize [`Kvm`] type for Aarch64 architecture + pub fn init_arch( + fd: KvmFd, + kvm_cap_modifiers: Vec, + ) -> Result { + Ok(Self { + fd, + kvm_cap_modifiers, + }) + } + + /// Returns struct with optional capabilities statuses. + pub fn optional_capabilities(&self) -> OptionalCapabilities { + OptionalCapabilities { + counter_offset: self + .fd + .check_extension_raw(kvm_bindings::KVM_CAP_COUNTER_OFFSET.into()) + != 0, + } + } +} diff --git a/src/vmm/src/arch/aarch64/mod.rs b/src/vmm/src/arch/aarch64/mod.rs index 08ca1b65edb..ead827c08c4 100644 --- a/src/vmm/src/arch/aarch64/mod.rs +++ b/src/vmm/src/arch/aarch64/mod.rs @@ -5,35 +5,50 @@ pub(crate) mod cache_info; mod fdt; /// Module for the global interrupt controller configuration. pub mod gic; +/// Architecture specific KVM-related code +pub mod kvm; /// Layout for this aarch64 system. pub mod layout; /// Logic for configuring aarch64 registers. pub mod regs; -/// Helper methods for VcpuFd. +/// Architecture specific vCPU code pub mod vcpu; +/// Architecture specific VM state code +pub mod vm; use std::cmp::min; -use std::collections::HashMap; -use std::ffi::CString; use std::fmt::Debug; +use std::fs::File; +use linux_loader::loader::pe::PE as Loader; +use linux_loader::loader::{Cmdline, KernelLoader}; use vm_memory::GuestMemoryError; -pub use self::fdt::DeviceInfoForFDT; -use self::gic::GICDevice; -use crate::arch::DeviceType; -use crate::devices::acpi::vmgenid::VmGenId; +use crate::arch::{BootProtocol, EntryPoint}; +use crate::cpu_config::aarch64::{CpuConfiguration, CpuConfigurationError}; +use crate::cpu_config::templates::CustomCpuTemplate; +use crate::initrd::InitrdConfig; +use crate::utils::{align_up, usize_to_u64}; +use crate::vmm_config::machine_config::MachineConfig; use crate::vstate::memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap}; +use crate::vstate::vcpu::KvmVcpuError; +use crate::{Vcpu, VcpuConfig, Vmm, logger}; /// Errors thrown while configuring aarch64 system. #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum ConfigurationError { /// Failed to create a Flattened Device Tree for this aarch64 microVM: {0} SetupFDT(#[from] fdt::FdtError), - /// Failed to compute the initrd address. - InitrdAddress, /// Failed to write to guest memory. - MemoryError(GuestMemoryError), + MemoryError(#[from] GuestMemoryError), + /// Cannot copy kernel file fd + KernelFile, + /// Cannot load kernel due to invalid memory configuration or invalid kernel image: {0} + KernelLoader(#[from] linux_loader::loader::Error), + /// Error creating vcpu configuration: {0} + VcpuConfig(#[from] CpuConfigurationError), + /// Error configuring the vcpu: {0} + VcpuConfigure(#[from] KvmVcpuError), } /// The start of the memory area reserved for MMIO devices. @@ -43,44 +58,93 @@ pub const MMIO_MEM_SIZE: u64 = layout::DRAM_MEM_START - layout::MAPPED_IO_START; /// Returns a Vec of the valid memory addresses for aarch64. /// See [`layout`](layout) module for a drawing of the specific memory model for this platform. -pub fn arch_memory_regions(size: usize) -> Vec<(GuestAddress, usize)> { - let dram_size = min(size, layout::DRAM_MEM_MAX_SIZE); - vec![(GuestAddress(layout::DRAM_MEM_START), dram_size)] +/// +/// The `offset` parameter specified the offset from [`layout::DRAM_MEM_START`]. +pub fn arch_memory_regions(offset: usize, size: usize) -> Vec<(GuestAddress, usize)> { + assert!(size > 0, "Attempt to allocate guest memory of length 0"); + assert!( + offset.checked_add(size).is_some(), + "Attempt to allocate guest memory such that the address space would wrap around" + ); + assert!( + offset < layout::DRAM_MEM_MAX_SIZE, + "offset outside allowed DRAM range" + ); + + let dram_size = min(size, layout::DRAM_MEM_MAX_SIZE - offset); + + if dram_size != size { + logger::warn!( + "Requested offset/memory size {}/{} exceeds architectural maximum (1022GiB). Size has \ + been truncated to {}", + offset, + size, + dram_size + ); + } + + vec![( + GuestAddress(layout::DRAM_MEM_START + offset as u64), + dram_size, + )] } -/// Configures the system and should be called once per vm before starting vcpu threads. -/// For aarch64, we only setup the FDT. -/// -/// # Arguments -/// -/// * `guest_mem` - The memory to be used by the guest. -/// * `cmdline_cstring` - The kernel commandline. -/// * `vcpu_mpidr` - Array of MPIDR register values per vcpu. -/// * `device_info` - A hashmap containing the attached devices for building FDT device nodes. -/// * `gic_device` - The GIC device. -/// * `initrd` - Information about an optional initrd. -pub fn configure_system( - guest_mem: &GuestMemoryMmap, - cmdline_cstring: CString, - vcpu_mpidr: Vec, - device_info: &HashMap<(DeviceType, String), T>, - gic_device: &GICDevice, - vmgenid: &Option, - initrd: &Option, +/// Configures the system for booting Linux. +pub fn configure_system_for_boot( + vmm: &mut Vmm, + vcpus: &mut [Vcpu], + machine_config: &MachineConfig, + cpu_template: &CustomCpuTemplate, + entry_point: EntryPoint, + initrd: &Option, + boot_cmdline: Cmdline, ) -> Result<(), ConfigurationError> { + // Construct the base CpuConfiguration to apply CPU template onto. + let cpu_config = CpuConfiguration::new(cpu_template, vcpus)?; + + // Apply CPU template to the base CpuConfiguration. + let cpu_config = CpuConfiguration::apply_template(cpu_config, cpu_template); + + let vcpu_config = VcpuConfig { + vcpu_count: machine_config.vcpu_count, + smt: machine_config.smt, + cpu_config, + }; + + let optional_capabilities = vmm.kvm.optional_capabilities(); + // Configure vCPUs with normalizing and setting the generated CPU configuration. + for vcpu in vcpus.iter_mut() { + vcpu.kvm_vcpu.configure( + vmm.vm.guest_memory(), + entry_point, + &vcpu_config, + &optional_capabilities, + )?; + } + let vcpu_mpidr = vcpus + .iter_mut() + .map(|cpu| cpu.kvm_vcpu.get_mpidr()) + .collect::, _>>() + .map_err(KvmVcpuError::ConfigureRegisters)?; + let cmdline = boot_cmdline + .as_cstring() + .expect("Cannot create cstring from cmdline string"); + let fdt = fdt::create_fdt( - guest_mem, + vmm.vm.guest_memory(), vcpu_mpidr, - cmdline_cstring, - device_info, - gic_device, - vmgenid, + cmdline, + vmm.mmio_device_manager.get_device_info(), + vmm.vm.get_irqchip(), + &vmm.acpi_device_manager.vmgenid, initrd, )?; - let fdt_address = GuestAddress(get_fdt_addr(guest_mem)); - guest_mem - .write_slice(fdt.as_slice(), fdt_address) - .map_err(ConfigurationError::MemoryError)?; + + let fdt_address = GuestAddress(get_fdt_addr(vmm.vm.guest_memory())); + vmm.vm + .guest_memory() + .write_slice(fdt.as_slice(), fdt_address)?; + Ok(()) } @@ -90,20 +154,20 @@ pub fn get_kernel_start() -> u64 { } /// Returns the memory address where the initrd could be loaded. -pub fn initrd_load_addr( - guest_mem: &GuestMemoryMmap, - initrd_size: usize, -) -> Result { - 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(initrd_size) as u64) { +pub fn initrd_load_addr(guest_mem: &GuestMemoryMmap, initrd_size: usize) -> Option { + let rounded_size = align_up( + usize_to_u64(initrd_size), + usize_to_u64(super::GUEST_PAGE_SIZE), + ); + match GuestAddress(get_fdt_addr(guest_mem)).checked_sub(rounded_size) { Some(offset) => { if guest_mem.address_in_range(offset) { - Ok(offset.raw_value()) + Some(offset.raw_value()) } else { - Err(ConfigurationError::InitrdAddress) + None } } - None => Err(ConfigurationError::InitrdAddress), + None => None, } } @@ -122,6 +186,69 @@ fn get_fdt_addr(mem: &GuestMemoryMmap) -> u64 { layout::DRAM_MEM_START } +/// Load linux kernel into guest memory. +pub fn load_kernel( + kernel: &File, + guest_memory: &GuestMemoryMmap, +) -> Result { + // Need to clone the File because reading from it + // mutates it. + let mut kernel_file = kernel + .try_clone() + .map_err(|_| ConfigurationError::KernelFile)?; + + let entry_addr = Loader::load( + guest_memory, + Some(GuestAddress(get_kernel_start())), + &mut kernel_file, + None, + )?; + + Ok(EntryPoint { + entry_addr: entry_addr.kernel_load, + protocol: BootProtocol::LinuxBoot, + }) +} + +#[cfg(kani)] +mod verification { + use vm_memory::GuestAddress; + + use crate::arch::aarch64::layout; + use crate::arch::arch_memory_regions; + + #[kani::proof] + #[kani::unwind(3)] + fn verify_arch_memory_regions() { + let offset: u64 = kani::any::(); + let len: u64 = kani::any::(); + + kani::assume(len > 0); + kani::assume(offset.checked_add(len).is_some()); + kani::assume(offset < layout::DRAM_MEM_MAX_SIZE as u64); + + let regions = arch_memory_regions(offset as usize, len as usize); + + // No MMIO gap on ARM + assert_eq!(regions.len(), 1); + + let (GuestAddress(start), actual_len) = regions[0]; + let actual_len = actual_len as u64; + + assert_eq!(start, layout::DRAM_MEM_START + offset); + assert!(actual_len <= layout::DRAM_MEM_MAX_SIZE as u64); + assert!(actual_len <= len); + + if actual_len < len { + assert_eq!( + start + actual_len, + layout::DRAM_MEM_START + layout::DRAM_MEM_MAX_SIZE as u64 + ); + assert!(offset + len >= layout::DRAM_MEM_MAX_SIZE as u64); + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -129,7 +256,7 @@ mod tests { #[test] fn test_regions_lt_1024gb() { - let regions = arch_memory_regions(1usize << 29); + let regions = arch_memory_regions(0, 1usize << 29); assert_eq!(1, regions.len()); assert_eq!(GuestAddress(super::layout::DRAM_MEM_START), regions[0].0); assert_eq!(1usize << 29, regions[0].1); @@ -137,7 +264,7 @@ mod tests { #[test] fn test_regions_gt_1024gb() { - let regions = arch_memory_regions(1usize << 41); + let regions = arch_memory_regions(0, 1usize << 41); assert_eq!(1, regions.len()); assert_eq!(GuestAddress(super::layout::DRAM_MEM_START), regions[0].0); assert_eq!(super::layout::DRAM_MEM_MAX_SIZE, regions[0].1); diff --git a/src/vmm/src/arch/aarch64/regs.rs b/src/vmm/src/arch/aarch64/regs.rs index 5238f58ba70..913f352a7d9 100644 --- a/src/vmm/src/arch/aarch64/regs.rs +++ b/src/vmm/src/arch/aarch64/regs.rs @@ -99,6 +99,12 @@ arm64_sys_reg!(SYS_CNTV_CVAL_EL0, 3, 3, 14, 3, 2); // https://elixir.bootlin.com/linux/v6.8/source/arch/arm64/include/asm/sysreg.h#L459 arm64_sys_reg!(SYS_CNTPCT_EL0, 3, 3, 14, 0, 1); +// Physical Timer EL0 count Register +// The id of this register is same as SYS_CNTPCT_EL0, but KVM defines it +// separately, so we do as well. +// https://elixir.bootlin.com/linux/v6.12.6/source/arch/arm64/include/uapi/asm/kvm.h#L259 +arm64_sys_reg!(KVM_REG_ARM_PTIMER_CNT, 3, 3, 14, 0, 1); + // Translation Table Base Register // https://developer.arm.com/documentation/ddi0595/2021-03/AArch64-Registers/TTBR1-EL1--Translation-Table-Base-Register-1--EL1- arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1); @@ -254,6 +260,14 @@ impl Aarch64RegisterVec { data: &mut self.data, } } + + /// Extract the Manufacturer ID from a VCPU state's registers. + /// The ID is found between bits 24-31 of MIDR_EL1 register. + pub fn manifacturer_id(&self) -> Option { + self.iter() + .find(|reg| reg.id == MIDR_EL1) + .map(|reg| ((reg.value::() >> 24) & 0xFF) as u32) + } } impl Serialize for Aarch64RegisterVec { diff --git a/src/vmm/src/arch/aarch64/vcpu.rs b/src/vmm/src/arch/aarch64/vcpu.rs index 5c08f95351a..6a418f59b5e 100644 --- a/src/vmm/src/arch/aarch64/vcpu.rs +++ b/src/vmm/src/arch/aarch64/vcpu.rs @@ -5,19 +5,30 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. +use std::fmt::{Debug, Write}; use std::mem::offset_of; use std::path::PathBuf; use kvm_bindings::*; -use kvm_ioctls::VcpuFd; +use kvm_ioctls::{VcpuExit, VcpuFd, VmFd}; +use serde::{Deserialize, Serialize}; use super::get_fdt_addr; use super::regs::*; -use crate::vstate::memory::GuestMemoryMmap; +use crate::arch::EntryPoint; +use crate::arch::aarch64::kvm::OptionalCapabilities; +use crate::arch::aarch64::regs::{Aarch64RegisterVec, KVM_REG_ARM64_SVE_VLS}; +use crate::cpu_config::aarch64::custom_cpu_template::VcpuFeatures; +use crate::cpu_config::templates::CpuConfiguration; +use crate::logger::{IncMetric, METRICS, error}; +use crate::vcpu::{VcpuConfig, VcpuError}; +use crate::vstate::memory::{Address, GuestMemoryMmap}; +use crate::vstate::vcpu::VcpuEmulation; +use crate::vstate::vm::Vm; /// Errors thrown while setting aarch64 registers. #[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] -pub enum VcpuError { +pub enum VcpuArchError { /// Failed to get register {0}: {1} GetOneReg(u64, kvm_ioctls::Error), /// Failed to set register {0}: {1} @@ -34,101 +45,23 @@ pub enum VcpuError { GetMidrEl1(String), } -/// Extract the Manufacturer ID from a VCPU state's registers. -/// The ID is found between bits 24-31 of MIDR_EL1 register. -/// -/// # Arguments -/// -/// * `regs` - reference [`Aarch64RegisterVec`] structure with all registers of a VCPU. -pub fn get_manufacturer_id_from_state(regs: &Aarch64RegisterVec) -> Result { - let midr_el1 = regs.iter().find(|reg| reg.id == MIDR_EL1); - match midr_el1 { - Some(register) => Ok(((register.value::() >> 24) & 0xFF) as u32), - None => Err(VcpuError::GetMidrEl1( - "Failed to find MIDR_EL1 in vCPU state!".to_string(), - )), - } -} - /// Extract the Manufacturer ID from the host. /// The ID is found between bits 24-31 of MIDR_EL1 register. -pub fn get_manufacturer_id_from_host() -> Result { +pub fn get_manufacturer_id_from_host() -> Result { let midr_el1_path = &PathBuf::from("/sys/devices/system/cpu/cpu0/regs/identification/midr_el1".to_string()); let midr_el1 = std::fs::read_to_string(midr_el1_path).map_err(|err| { - VcpuError::GetMidrEl1(format!("Failed to get MIDR_EL1 from host path: {err}")) + VcpuArchError::GetMidrEl1(format!("Failed to get MIDR_EL1 from host path: {err}")) })?; let midr_el1_trimmed = midr_el1.trim_end().trim_start_matches("0x"); - let manufacturer_id = u32::from_str_radix(midr_el1_trimmed, 16) - .map_err(|err| VcpuError::GetMidrEl1(format!("Invalid MIDR_EL1 found on host: {err}",)))?; + let manufacturer_id = u32::from_str_radix(midr_el1_trimmed, 16).map_err(|err| { + VcpuArchError::GetMidrEl1(format!("Invalid MIDR_EL1 found on host: {err}",)) + })?; Ok(manufacturer_id >> 24) } -/// Configure relevant boot registers for a given vCPU. -/// -/// # Arguments -/// -/// * `cpu_id` - Index of current vcpu. -/// * `boot_ip` - Starting instruction pointer. -/// * `mem` - Reserved DRAM for current VM. -pub fn setup_boot_regs( - vcpufd: &VcpuFd, - cpu_id: u8, - boot_ip: u64, - mem: &GuestMemoryMmap, -) -> Result<(), VcpuError> { - let kreg_off = offset_of!(kvm_regs, regs); - - // Get the register index of the PSTATE (Processor State) register. - let pstate = offset_of!(user_pt_regs, pstate) + kreg_off; - let id = arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate); - vcpufd - .set_one_reg(id, &PSTATE_FAULT_BITS_64.to_le_bytes()) - .map_err(|err| VcpuError::SetOneReg(id, err))?; - - // Other vCPUs are powered off initially awaiting PSCI wakeup. - if cpu_id == 0 { - // Setting the PC (Processor Counter) to the current program address (kernel address). - let pc = offset_of!(user_pt_regs, pc) + kreg_off; - let id = arm64_core_reg_id!(KVM_REG_SIZE_U64, pc); - vcpufd - .set_one_reg(id, &boot_ip.to_le_bytes()) - .map_err(|err| VcpuError::SetOneReg(id, err))?; - - // Last mandatory thing to set -> the address pointing to the FDT (also called DTB). - // "The device tree blob (dtb) must be placed on an 8-byte boundary and must - // not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt. - // We are choosing to place it the end of DRAM. See `get_fdt_addr`. - let regs0 = offset_of!(user_pt_regs, regs) + kreg_off; - let id = arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0); - vcpufd - .set_one_reg(id, &get_fdt_addr(mem).to_le_bytes()) - .map_err(|err| VcpuError::SetOneReg(id, err))?; - } - Ok(()) -} - -/// Read the MPIDR - Multiprocessor Affinity Register. -pub fn get_mpidr(vcpufd: &VcpuFd) -> Result { - // MPIDR register is 64 bit wide on aarch64 - let mut mpidr = [0_u8; 8]; - match vcpufd.get_one_reg(MPIDR_EL1, &mut mpidr) { - Err(err) => Err(VcpuError::GetOneReg(MPIDR_EL1, err)), - Ok(_) => Ok(u64::from_le_bytes(mpidr)), - } -} - -/// Saves the states of the system registers into `state`. -/// -/// # Arguments -/// -/// * `regs` - Input/Output vector of registers. -pub fn get_all_registers(vcpufd: &VcpuFd, state: &mut Aarch64RegisterVec) -> Result<(), VcpuError> { - get_registers(vcpufd, &get_all_registers_ids(vcpufd)?, state) -} - /// Saves states of registers into `state`. /// /// # Arguments @@ -136,146 +69,692 @@ pub fn get_all_registers(vcpufd: &VcpuFd, state: &mut Aarch64RegisterVec) -> Res /// * `ids` - Slice of registers ids to save. /// * `regs` - Input/Output vector of registers. pub fn get_registers( - vcpufd: &VcpuFd, + vcpu_fd: &VcpuFd, ids: &[u64], regs: &mut Aarch64RegisterVec, -) -> Result<(), VcpuError> { +) -> Result<(), VcpuArchError> { let mut big_reg = [0_u8; 256]; for id in ids.iter() { - let reg_size = vcpufd + let reg_size = vcpu_fd .get_one_reg(*id, &mut big_reg) - .map_err(|e| VcpuError::GetOneReg(*id, e))?; + .map_err(|e| VcpuArchError::GetOneReg(*id, e))?; let reg_ref = Aarch64RegisterRef::new(*id, &big_reg[0..reg_size]); regs.push(reg_ref); } Ok(()) } -/// Returns all registers ids, including core and system -pub fn get_all_registers_ids(vcpufd: &VcpuFd) -> Result, VcpuError> { - // Call KVM_GET_REG_LIST to get all registers available to the guest. For ArmV8 there are - // less than 500 registers expected, resize to the reported size when necessary. - let mut reg_list = RegList::new(500).map_err(VcpuError::Fam)?; - - match vcpufd.get_reg_list(&mut reg_list) { - Ok(_) => Ok(reg_list.as_slice().to_vec()), - Err(e) => match e.errno() { - libc::E2BIG => { - // resize and retry. - let size: usize = reg_list - .as_fam_struct_ref() - .n - .try_into() - // Safe to unwrap as Firecracker only targets 64-bit machines. - .unwrap(); - reg_list = RegList::new(size).map_err(VcpuError::Fam)?; - vcpufd - .get_reg_list(&mut reg_list) - .map_err(VcpuError::GetRegList)?; - - Ok(reg_list.as_slice().to_vec()) +/// Errors associated with the wrappers over KVM ioctls. +#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] +pub enum KvmVcpuError { + /// Error configuring the vcpu registers: {0} + ConfigureRegisters(VcpuArchError), + /// Error creating vcpu: {0} + CreateVcpu(kvm_ioctls::Error), + /// Failed to dump CPU configuration: {0} + DumpCpuConfig(VcpuArchError), + /// Error getting the vcpu preferred target: {0} + GetPreferredTarget(kvm_ioctls::Error), + /// Error initializing the vcpu: {0} + Init(kvm_ioctls::Error), + /// Error applying template: {0} + ApplyCpuTemplate(VcpuArchError), + /// Failed to restore the state of the vcpu: {0} + RestoreState(VcpuArchError), + /// Failed to save the state of the vcpu: {0} + SaveState(VcpuArchError), +} + +/// Error type for [`KvmVcpu::configure`]. +pub type KvmVcpuConfigureError = KvmVcpuError; + +/// A wrapper around creating and using a kvm aarch64 vcpu. +#[derive(Debug)] +pub struct KvmVcpu { + /// Index of vcpu. + pub index: u8, + /// KVM vcpu fd. + pub fd: VcpuFd, + /// Vcpu peripherals, such as buses + pub peripherals: Peripherals, + kvi: kvm_vcpu_init, +} + +/// Vcpu peripherals +#[derive(Default, Debug)] +pub struct Peripherals { + /// mmio bus. + pub mmio_bus: Option, +} + +impl KvmVcpu { + /// Constructs a new kvm vcpu with arch specific functionality. + /// + /// # Arguments + /// + /// * `index` - Represents the 0-based CPU index between [0, max vcpus). + /// * `vm` - The vm to which this vcpu will get attached. + pub fn new(index: u8, vm: &Vm) -> Result { + let kvm_vcpu = vm + .fd() + .create_vcpu(index.into()) + .map_err(KvmVcpuError::CreateVcpu)?; + + let mut kvi = Self::default_kvi(vm.fd())?; + // Secondary vcpus must be powered off for boot process. + if 0 < index { + kvi.features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF; + } + + Ok(KvmVcpu { + index, + fd: kvm_vcpu, + peripherals: Default::default(), + kvi, + }) + } + + /// Read the MPIDR - Multiprocessor Affinity Register. + pub fn get_mpidr(&self) -> Result { + // MPIDR register is 64 bit wide on aarch64 + let mut mpidr = [0_u8; 8]; + match self.fd.get_one_reg(MPIDR_EL1, &mut mpidr) { + Err(err) => Err(VcpuArchError::GetOneReg(MPIDR_EL1, err)), + Ok(_) => Ok(u64::from_le_bytes(mpidr)), + } + } + + /// Configures an aarch64 specific vcpu for booting Linux. + /// + /// # Arguments + /// + /// * `guest_mem` - The guest memory used by this microvm. + /// * `kernel_entry_point` - Specifies the boot protocol and offset from `guest_mem` at which + /// the kernel starts. + /// * `vcpu_config` - The vCPU configuration. + pub fn configure( + &mut self, + guest_mem: &GuestMemoryMmap, + kernel_entry_point: EntryPoint, + vcpu_config: &VcpuConfig, + optional_capabilities: &OptionalCapabilities, + ) -> Result<(), KvmVcpuError> { + for reg in vcpu_config.cpu_config.regs.iter() { + self.fd.set_one_reg(reg.id, reg.as_slice()).map_err(|err| { + KvmVcpuError::ApplyCpuTemplate(VcpuArchError::SetOneReg(reg.id, err)) + })?; + } + + self.setup_boot_regs( + kernel_entry_point.entry_addr.raw_value(), + guest_mem, + optional_capabilities, + ) + .map_err(KvmVcpuError::ConfigureRegisters)?; + + Ok(()) + } + + /// Initializes an aarch64 specific vcpu for booting Linux. + /// + /// # Arguments + /// + /// * `vm_fd` - The kvm `VmFd` for this microvm. + pub fn init(&mut self, vcpu_features: &[VcpuFeatures]) -> Result<(), KvmVcpuError> { + for feature in vcpu_features.iter() { + let index = feature.index as usize; + self.kvi.features[index] = feature.bitmap.apply(self.kvi.features[index]); + } + + self.init_vcpu()?; + self.finalize_vcpu()?; + + Ok(()) + } + + /// Creates default kvi struct based on vcpu index. + pub fn default_kvi(vm_fd: &VmFd) -> Result { + let mut kvi = kvm_vcpu_init::default(); + // This reads back the kernel's preferred target type. + vm_fd + .get_preferred_target(&mut kvi) + .map_err(KvmVcpuError::GetPreferredTarget)?; + // We already checked that the capability is supported. + kvi.features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; + + Ok(kvi) + } + + /// Save the KVM internal state. + pub fn save_state(&self) -> Result { + let mut state = VcpuState { + mp_state: self.get_mpstate().map_err(KvmVcpuError::SaveState)?, + ..Default::default() + }; + self.get_all_registers(&mut state.regs) + .map_err(KvmVcpuError::SaveState)?; + state.mpidr = self.get_mpidr().map_err(KvmVcpuError::SaveState)?; + + state.kvi = self.kvi; + // We don't save power off state in a snapshot, because + // it was only needed during uVM boot process. + // When uVM is restored, the kernel has already passed + // the boot state and turned secondary vcpus on. + state.kvi.features[0] &= !(1 << KVM_ARM_VCPU_POWER_OFF); + + Ok(state) + } + + /// Use provided state to populate KVM internal state. + pub fn restore_state(&mut self, state: &VcpuState) -> Result<(), KvmVcpuError> { + self.kvi = state.kvi; + + self.init_vcpu()?; + + // If KVM_REG_ARM64_SVE_VLS is present it needs to + // be set before vcpu is finalized. + if let Some(sve_vls_reg) = state + .regs + .iter() + .find(|reg| reg.id == KVM_REG_ARM64_SVE_VLS) + { + self.set_register(sve_vls_reg) + .map_err(KvmVcpuError::RestoreState)?; + } + + self.finalize_vcpu()?; + + // KVM_REG_ARM64_SVE_VLS needs to be skipped after vcpu is finalized. + // If it is present it is handled in the code above. + for reg in state + .regs + .iter() + .filter(|reg| reg.id != KVM_REG_ARM64_SVE_VLS) + { + self.set_register(reg).map_err(KvmVcpuError::RestoreState)?; + } + self.set_mpstate(state.mp_state) + .map_err(KvmVcpuError::RestoreState)?; + Ok(()) + } + + /// Dumps CPU configuration. + pub fn dump_cpu_config(&self) -> Result { + let mut regs = Aarch64RegisterVec::default(); + self.get_all_registers(&mut regs) + .map_err(KvmVcpuError::DumpCpuConfig)?; + Ok(CpuConfiguration { regs }) + } + + /// Initializes internal vcpufd. + fn init_vcpu(&self) -> Result<(), KvmVcpuError> { + self.fd.vcpu_init(&self.kvi).map_err(KvmVcpuError::Init)?; + Ok(()) + } + + /// Checks for SVE feature and calls `vcpu_finalize` if + /// it is enabled. + fn finalize_vcpu(&self) -> Result<(), KvmVcpuError> { + if (self.kvi.features[0] & (1 << KVM_ARM_VCPU_SVE)) != 0 { + // KVM_ARM_VCPU_SVE has value 4 so casting to i32 is safe. + #[allow(clippy::cast_possible_wrap)] + let feature = KVM_ARM_VCPU_SVE as i32; + self.fd.vcpu_finalize(&feature).unwrap(); + } + Ok(()) + } + + /// Configure relevant boot registers for a given vCPU. + /// + /// # Arguments + /// + /// * `boot_ip` - Starting instruction pointer. + /// * `mem` - Reserved DRAM for current VM. + /// + `optional_capabilities` - which optional capabilities are enabled that might influence + /// vcpu configuration + pub fn setup_boot_regs( + &self, + boot_ip: u64, + mem: &GuestMemoryMmap, + optional_capabilities: &OptionalCapabilities, + ) -> Result<(), VcpuArchError> { + let kreg_off = offset_of!(kvm_regs, regs); + + // Get the register index of the PSTATE (Processor State) register. + let pstate = offset_of!(user_pt_regs, pstate) + kreg_off; + let id = arm64_core_reg_id!(KVM_REG_SIZE_U64, pstate); + self.fd + .set_one_reg(id, &PSTATE_FAULT_BITS_64.to_le_bytes()) + .map_err(|err| VcpuArchError::SetOneReg(id, err))?; + + // Other vCPUs are powered off initially awaiting PSCI wakeup. + if self.index == 0 { + // Setting the PC (Processor Counter) to the current program address (kernel address). + let pc = offset_of!(user_pt_regs, pc) + kreg_off; + let id = arm64_core_reg_id!(KVM_REG_SIZE_U64, pc); + self.fd + .set_one_reg(id, &boot_ip.to_le_bytes()) + .map_err(|err| VcpuArchError::SetOneReg(id, err))?; + + // Last mandatory thing to set -> the address pointing to the FDT (also called DTB). + // "The device tree blob (dtb) must be placed on an 8-byte boundary and must + // not exceed 2 megabytes in size." -> https://www.kernel.org/doc/Documentation/arm64/booting.txt. + // We are choosing to place it the end of DRAM. See `get_fdt_addr`. + let regs0 = offset_of!(user_pt_regs, regs) + kreg_off; + let id = arm64_core_reg_id!(KVM_REG_SIZE_U64, regs0); + self.fd + .set_one_reg(id, &get_fdt_addr(mem).to_le_bytes()) + .map_err(|err| VcpuArchError::SetOneReg(id, err))?; + + // Reset the physical counter for the guest. This way we avoid guest reading + // host physical counter. + // Resetting KVM_REG_ARM_PTIMER_CNT for single vcpu is enough because there is only + // one timer struct with offsets per VM. + // Because the access to KVM_REG_ARM_PTIMER_CNT is only present starting 6.4 kernel, + // we only do the reset if KVM_CAP_COUNTER_OFFSET is present as it was added + // in the same patch series as the ability to set the KVM_REG_ARM_PTIMER_CNT register. + // Path series which introduced the needed changes: + // https://lore.kernel.org/all/20230330174800.2677007-1-maz@kernel.org/ + // Note: the value observed by the guest will still be above 0, because there is a delta + // time between this resetting and first call to KVM_RUN. + if optional_capabilities.counter_offset { + self.fd + .set_one_reg(KVM_REG_ARM_PTIMER_CNT, &[0; 8]) + .map_err(|err| VcpuArchError::SetOneReg(id, err))?; } - _ => Err(VcpuError::GetRegList(e)), - }, + } + Ok(()) + } + + /// Saves the states of the system registers into `state`. + /// + /// # Arguments + /// + /// * `regs` - Input/Output vector of registers. + pub fn get_all_registers(&self, state: &mut Aarch64RegisterVec) -> Result<(), VcpuArchError> { + get_registers(&self.fd, &self.get_all_registers_ids()?, state) + } + + /// Returns all registers ids, including core and system + pub fn get_all_registers_ids(&self) -> Result, VcpuArchError> { + // Call KVM_GET_REG_LIST to get all registers available to the guest. For ArmV8 there are + // less than 500 registers expected, resize to the reported size when necessary. + let mut reg_list = RegList::new(500).map_err(VcpuArchError::Fam)?; + + match self.fd.get_reg_list(&mut reg_list) { + Ok(_) => Ok(reg_list.as_slice().to_vec()), + Err(e) => match e.errno() { + libc::E2BIG => { + // resize and retry. + let size: usize = reg_list + .as_fam_struct_ref() + .n + .try_into() + // Safe to unwrap as Firecracker only targets 64-bit machines. + .unwrap(); + reg_list = RegList::new(size).map_err(VcpuArchError::Fam)?; + self.fd + .get_reg_list(&mut reg_list) + .map_err(VcpuArchError::GetRegList)?; + + Ok(reg_list.as_slice().to_vec()) + } + _ => Err(VcpuArchError::GetRegList(e)), + }, + } + } + + /// Set the state of one system register. + /// + /// # Arguments + /// + /// * `reg` - Register to be set. + pub fn set_register(&self, reg: Aarch64RegisterRef) -> Result<(), VcpuArchError> { + self.fd + .set_one_reg(reg.id, reg.as_slice()) + .map_err(|e| VcpuArchError::SetOneReg(reg.id, e))?; + Ok(()) + } + + /// Get the multistate processor. + /// + /// # Arguments + /// + /// * `vcpu` - Structure for the VCPU that holds the VCPU's fd. + pub fn get_mpstate(&self) -> Result { + self.fd.get_mp_state().map_err(VcpuArchError::GetMp) + } + + /// Set the state of the system registers. + /// + /// # Arguments + /// + /// * `state` - Structure for returning the state of the system registers. + pub fn set_mpstate(&self, state: kvm_mp_state) -> Result<(), VcpuArchError> { + self.fd.set_mp_state(state).map_err(VcpuArchError::SetMp) } } -/// Set the state of one system register. -/// -/// # Arguments -/// -/// * `reg` - Register to be set. -pub fn set_register(vcpufd: &VcpuFd, reg: Aarch64RegisterRef) -> Result<(), VcpuError> { - vcpufd - .set_one_reg(reg.id, reg.as_slice()) - .map_err(|e| VcpuError::SetOneReg(reg.id, e))?; - Ok(()) +impl Peripherals { + /// Runs the vCPU in KVM context and handles the kvm exit reason. + /// + /// Returns error or enum specifying whether emulation was handled or interrupted. + pub fn run_arch_emulation(&self, exit: VcpuExit) -> Result { + METRICS.vcpu.failures.inc(); + // TODO: Are we sure we want to finish running a vcpu upon + // receiving a vm exit that is not necessarily an error? + error!("Unexpected exit reason on vcpu run: {:?}", exit); + Err(VcpuError::UnhandledKvmExit(format!("{:?}", exit))) + } } -/// Get the multistate processor. -/// -/// # Arguments -/// -/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd. -pub fn get_mpstate(vcpufd: &VcpuFd) -> Result { - vcpufd.get_mp_state().map_err(VcpuError::GetMp) +/// Structure holding VCPU kvm state. +#[derive(Default, Clone, Serialize, Deserialize)] +pub struct VcpuState { + /// Multiprocessing state. + pub mp_state: kvm_mp_state, + /// Vcpu registers. + pub regs: Aarch64RegisterVec, + /// We will be using the mpidr for passing it to the VmState. + /// The VmState will give this away for saving restoring the icc and redistributor + /// registers. + pub mpidr: u64, + /// kvi states for vcpu initialization. + pub kvi: kvm_vcpu_init, } -/// Set the state of the system registers. -/// -/// # Arguments -/// -/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd. -/// * `state` - Structure for returning the state of the system registers. -pub fn set_mpstate(vcpufd: &VcpuFd, state: kvm_mp_state) -> Result<(), VcpuError> { - vcpufd.set_mp_state(state).map_err(VcpuError::SetMp) +impl Debug for VcpuState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "kvm_mp_state: {:#x}", self.mp_state.mp_state)?; + writeln!(f, "mpidr: {:#x}", self.mpidr)?; + for reg in self.regs.iter() { + writeln!( + f, + "{:#x} 0x{}", + reg.id, + reg.as_slice() + .iter() + .rev() + .fold(String::new(), |mut output, b| { + let _ = write!(output, "{b:x}"); + output + }) + )?; + } + Ok(()) + } } #[cfg(test)] mod tests { #![allow(clippy::undocumented_unsafe_blocks)] - use kvm_ioctls::Kvm; + use std::os::unix::io::AsRawFd; + + use kvm_bindings::{KVM_ARM_VCPU_PSCI_0_2, KVM_REG_SIZE_U64}; + use vm_memory::GuestAddress; use super::*; + use crate::arch::BootProtocol; use crate::arch::aarch64::layout; + use crate::arch::aarch64::regs::Aarch64RegisterRef; + use crate::cpu_config::aarch64::CpuConfiguration; + use crate::cpu_config::templates::RegisterValueFilter; use crate::test_utils::arch_mem; + use crate::vcpu::VcpuConfig; + use crate::vstate::kvm::Kvm; + use crate::vstate::vm::Vm; + use crate::vstate::vm::tests::setup_vm_with_memory; + + fn setup_vcpu(mem_size: usize) -> (Kvm, Vm, KvmVcpu) { + let (kvm, mut vm, mut vcpu) = setup_vcpu_no_init(mem_size); + vcpu.init(&[]).unwrap(); + vm.setup_irqchip(1).unwrap(); + (kvm, vm, vcpu) + } + + fn setup_vcpu_no_init(mem_size: usize) -> (Kvm, Vm, KvmVcpu) { + let (kvm, vm) = setup_vm_with_memory(mem_size); + let vcpu = KvmVcpu::new(0, &vm).unwrap(); + + (kvm, vm, vcpu) + } + + #[test] + fn test_create_vcpu() { + let (_, vm) = setup_vm_with_memory(0x1000); + + unsafe { libc::close(vm.fd().as_raw_fd()) }; + + let err = KvmVcpu::new(0, &vm); + assert_eq!( + err.err().unwrap().to_string(), + "Error creating vcpu: Bad file descriptor (os error 9)".to_string() + ); + + // dropping vm would double close the gic fd, so leak it + std::mem::forget(vm); + } + + #[test] + fn test_configure_vcpu() { + let (kvm, vm, mut vcpu) = setup_vcpu(0x10000); + let optional_capabilities = kvm.optional_capabilities(); + + let vcpu_config = VcpuConfig { + vcpu_count: 1, + smt: false, + cpu_config: CpuConfiguration::default(), + }; + + vcpu.configure( + vm.guest_memory(), + EntryPoint { + entry_addr: GuestAddress(crate::arch::get_kernel_start()), + protocol: BootProtocol::LinuxBoot, + }, + &vcpu_config, + &optional_capabilities, + ) + .unwrap(); + + unsafe { libc::close(vcpu.fd.as_raw_fd()) }; + + let err = vcpu.configure( + vm.guest_memory(), + EntryPoint { + entry_addr: GuestAddress(crate::arch::get_kernel_start()), + protocol: BootProtocol::LinuxBoot, + }, + &vcpu_config, + &optional_capabilities, + ); + assert_eq!( + err.unwrap_err(), + KvmVcpuError::ConfigureRegisters(VcpuArchError::SetOneReg( + 0x6030000000100042, + kvm_ioctls::Error::new(9) + )) + ); + + // dropping vcpu would double close the gic fd, so leak it + std::mem::forget(vcpu); + } + + #[test] + fn test_init_vcpu() { + let (_, mut vm) = setup_vm_with_memory(0x1000); + let mut vcpu = KvmVcpu::new(0, &vm).unwrap(); + vm.setup_irqchip(1).unwrap(); + + // KVM_ARM_VCPU_PSCI_0_2 is set by default. + // we check if we can remove it. + let vcpu_features = vec![VcpuFeatures { + index: 0, + bitmap: RegisterValueFilter { + filter: 1 << KVM_ARM_VCPU_PSCI_0_2, + value: 0, + }, + }]; + vcpu.init(&vcpu_features).unwrap(); + assert!((vcpu.kvi.features[0] & (1 << KVM_ARM_VCPU_PSCI_0_2)) == 0) + } + + #[test] + fn test_vcpu_save_restore_state() { + let (_, mut vm) = setup_vm_with_memory(0x1000); + let mut vcpu = KvmVcpu::new(0, &vm).unwrap(); + vm.setup_irqchip(1).unwrap(); + + // Calling KVM_GET_REGLIST before KVM_VCPU_INIT will result in error. + let res = vcpu.save_state(); + assert!(matches!( + res.unwrap_err(), + KvmVcpuError::SaveState(VcpuArchError::GetRegList(_)) + )); + + // Try to restore the register using a faulty state. + let mut faulty_vcpu_state = VcpuState::default(); + + // Try faulty kvi state + let res = vcpu.restore_state(&faulty_vcpu_state); + assert!(matches!(res.unwrap_err(), KvmVcpuError::Init(_))); + + // Try faulty vcpu regs + faulty_vcpu_state.kvi = KvmVcpu::default_kvi(vm.fd()).unwrap(); + let mut regs = Aarch64RegisterVec::default(); + let mut reg = Aarch64RegisterRef::new(KVM_REG_SIZE_U64, &[0; 8]); + reg.id = 0; + regs.push(reg); + faulty_vcpu_state.regs = regs; + let res = vcpu.restore_state(&faulty_vcpu_state); + assert!(matches!( + res.unwrap_err(), + KvmVcpuError::RestoreState(VcpuArchError::SetOneReg(0, _)) + )); + + vcpu.init(&[]).unwrap(); + let state = vcpu.save_state().expect("Cannot save state of vcpu"); + assert!(!state.regs.is_empty()); + vcpu.restore_state(&state) + .expect("Cannot restore state of vcpu"); + } + + #[test] + fn test_dump_cpu_config_before_init() { + // Test `dump_cpu_config()` before `KVM_VCPU_INIT`. + // + // This should fail with ENOEXEC. + // https://elixir.bootlin.com/linux/v5.10.176/source/arch/arm64/kvm/arm.c#L1165 + let (_, mut vm) = setup_vm_with_memory(0x1000); + let vcpu = KvmVcpu::new(0, &vm).unwrap(); + vm.setup_irqchip(1).unwrap(); + + vcpu.dump_cpu_config().unwrap_err(); + } + + #[test] + fn test_dump_cpu_config_after_init() { + // Test `dump_cpu_config()` after `KVM_VCPU_INIT`. + let (_, mut vm) = setup_vm_with_memory(0x1000); + let mut vcpu = KvmVcpu::new(0, &vm).unwrap(); + vm.setup_irqchip(1).unwrap(); + vcpu.init(&[]).unwrap(); + + vcpu.dump_cpu_config().unwrap(); + } + + #[test] + fn test_setup_non_boot_vcpu() { + let (_, vm) = setup_vm_with_memory(0x1000); + let mut vcpu1 = KvmVcpu::new(0, &vm).unwrap(); + vcpu1.init(&[]).unwrap(); + let mut vcpu2 = KvmVcpu::new(1, &vm).unwrap(); + vcpu2.init(&[]).unwrap(); + } + + #[test] + fn test_get_valid_regs() { + // Test `get_regs()` with valid register IDs. + // - X0: 0x6030 0000 0010 0000 + // - X1: 0x6030 0000 0010 0002 + let (_, _, vcpu) = setup_vcpu(0x10000); + let reg_list = Vec::::from([0x6030000000100000, 0x6030000000100002]); + get_registers(&vcpu.fd, ®_list, &mut Aarch64RegisterVec::default()).unwrap(); + } + + #[test] + fn test_get_invalid_regs() { + // Test `get_regs()` with invalid register IDs. + let (_, _, vcpu) = setup_vcpu(0x10000); + let reg_list = Vec::::from([0x6030000000100001, 0x6030000000100003]); + get_registers(&vcpu.fd, ®_list, &mut Aarch64RegisterVec::default()).unwrap_err(); + } #[test] fn test_setup_regs() { - let kvm = Kvm::new().unwrap(); - let vm = kvm.create_vm().unwrap(); - let vcpu = vm.create_vcpu(0).unwrap(); + let (kvm, _, vcpu) = setup_vcpu_no_init(0x10000); let mem = arch_mem(layout::FDT_MAX_SIZE + 0x1000); + let optional_capabilities = kvm.optional_capabilities(); - let res = setup_boot_regs(&vcpu, 0, 0x0, &mem); + let res = vcpu.setup_boot_regs(0x0, &mem, &optional_capabilities); assert!(matches!( res.unwrap_err(), - VcpuError::SetOneReg(0x6030000000100042, _) + VcpuArchError::SetOneReg(0x6030000000100042, _) )); - 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(); + vcpu.init_vcpu().unwrap(); + + vcpu.setup_boot_regs(0x0, &mem, &optional_capabilities) + .unwrap(); - setup_boot_regs(&vcpu, 0, 0x0, &mem).unwrap(); + // Check that the register is reset on compatible kernels. + // Because there is a delta in time between we reset the register and time we + // read it, we cannot compare with 0. Instead we compare it with meaningfully + // small value. + if optional_capabilities.counter_offset { + let mut reg_bytes = [0_u8; 8]; + vcpu.fd.get_one_reg(SYS_CNTPCT_EL0, &mut reg_bytes).unwrap(); + let counter_value = u64::from_le_bytes(reg_bytes); + + // We are reading the SYS_CNTPCT_EL0 right after resetting it. + // If reset did happen successfully, the value should be quite small when we read it. + // If the reset did not happen, the value will be same as on the host and it surely + // will be more that `max_value`. Measurements show that usually value is close + // to 1000. Use bigger `max_value` just in case. + let max_value = 10_000; + + assert!(counter_value < max_value); + } } #[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(); + let (_, _, vcpu) = setup_vcpu_no_init(0x10000); // Must fail when vcpu is not initialized yet. - let res = get_mpidr(&vcpu); + let res = vcpu.get_mpidr(); assert!(matches!( res.unwrap_err(), - VcpuError::GetOneReg(MPIDR_EL1, _) + VcpuArchError::GetOneReg(MPIDR_EL1, _) )); + vcpu.init_vcpu().unwrap(); - vcpu.vcpu_init(&kvi).unwrap(); - assert_eq!(get_mpidr(&vcpu).unwrap(), 0x8000_0000); + assert_eq!(vcpu.get_mpidr().unwrap(), 0x8000_0000); } #[test] fn test_get_set_regs() { - 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(); + let (_, _, vcpu) = setup_vcpu_no_init(0x10000); // Must fail when vcpu is not initialized yet. let mut regs = Aarch64RegisterVec::default(); - let res = get_all_registers(&vcpu, &mut regs); - assert!(matches!(res.unwrap_err(), VcpuError::GetRegList(_))); + let res = vcpu.get_all_registers(&mut regs); + assert!(matches!(res.unwrap_err(), VcpuArchError::GetRegList(_))); + vcpu.init_vcpu().unwrap(); - vcpu.vcpu_init(&kvi).unwrap(); - get_all_registers(&vcpu, &mut regs).unwrap(); + vcpu.get_all_registers(&mut regs).unwrap(); for reg in regs.iter() { - set_register(&vcpu, reg).unwrap(); + vcpu.set_register(reg).unwrap(); } } @@ -283,21 +762,20 @@ mod tests { fn test_mpstate() { use std::os::unix::io::AsRawFd; - 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(); + let (_, _, vcpu) = setup_vcpu(0x10000); + + let res = vcpu.get_mpstate(); + vcpu.set_mpstate(res.unwrap()).unwrap(); - let res = get_mpstate(&vcpu); - set_mpstate(&vcpu, res.unwrap()).unwrap(); + unsafe { libc::close(vcpu.fd.as_raw_fd()) }; - unsafe { libc::close(vcpu.as_raw_fd()) }; + let res = vcpu.get_mpstate(); + assert!(matches!(res, Err(VcpuArchError::GetMp(_))), "{:?}", res); - let res = get_mpstate(&vcpu); - assert!(matches!(res, Err(VcpuError::GetMp(_))), "{:?}", res); + let res = vcpu.set_mpstate(kvm_mp_state::default()); + assert!(matches!(res, Err(VcpuArchError::SetMp(_))), "{:?}", res); - let res = set_mpstate(&vcpu, kvm_mp_state::default()); - assert!(matches!(res, Err(VcpuError::SetMp(_))), "{:?}", res); + // dropping vcpu would double close the fd, so leak it + std::mem::forget(vcpu); } } diff --git a/src/vmm/src/arch/aarch64/vm.rs b/src/vmm/src/arch/aarch64/vm.rs new file mode 100644 index 00000000000..e54723f5b6d --- /dev/null +++ b/src/vmm/src/arch/aarch64/vm.rs @@ -0,0 +1,101 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; + +use crate::Kvm; +use crate::arch::aarch64::gic::GicState; +use crate::vstate::memory::{GuestMemoryExtension, GuestMemoryState}; +use crate::vstate::vm::{VmCommon, VmError}; + +/// Structure representing the current architecture's understand of what a "virtual machine" is. +#[derive(Debug)] +pub struct ArchVm { + /// Architecture independent parts of a vm. + pub common: VmCommon, + // On aarch64 we need to keep around the fd obtained by creating the VGIC device. + irqchip_handle: Option, +} + +/// Error type for [`Vm::restore_state`] +#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] +pub enum ArchVmError { + /// Error creating the global interrupt controller: {0} + VmCreateGIC(crate::arch::aarch64::gic::GicError), + /// Failed to save the VM's GIC state: {0} + SaveGic(crate::arch::aarch64::gic::GicError), + /// Failed to restore the VM's GIC state: {0} + RestoreGic(crate::arch::aarch64::gic::GicError), +} + +impl ArchVm { + /// Create a new `Vm` struct. + pub fn new(kvm: &Kvm) -> Result { + let common = Self::create_common(kvm)?; + Ok(ArchVm { + common, + irqchip_handle: None, + }) + } + + /// Pre-vCPU creation setup. + pub fn arch_pre_create_vcpus(&mut self, _: u8) -> Result<(), ArchVmError> { + Ok(()) + } + + /// Post-vCPU creation setup. + pub fn arch_post_create_vcpus(&mut self, nr_vcpus: u8) -> Result<(), ArchVmError> { + // On aarch64, the vCPUs need to be created (i.e call KVM_CREATE_VCPU) before setting up the + // IRQ chip because the `KVM_CREATE_VCPU` ioctl will return error if the IRQCHIP + // was already initialized. + // Search for `kvm_arch_vcpu_create` in arch/arm/kvm/arm.c. + self.setup_irqchip(nr_vcpus) + } + + /// Creates the GIC (Global Interrupt Controller). + pub fn setup_irqchip(&mut self, vcpu_count: u8) -> Result<(), ArchVmError> { + self.irqchip_handle = Some( + crate::arch::aarch64::gic::create_gic(self.fd(), vcpu_count.into(), None) + .map_err(ArchVmError::VmCreateGIC)?, + ); + Ok(()) + } + + /// Gets a reference to the irqchip of the VM. + pub fn get_irqchip(&self) -> &crate::arch::aarch64::gic::GICDevice { + self.irqchip_handle.as_ref().expect("IRQ chip not set") + } + + /// Saves and returns the Kvm Vm state. + pub fn save_state(&self, mpidrs: &[u64]) -> Result { + Ok(VmState { + memory: self.common.guest_memory.describe(), + gic: self + .get_irqchip() + .save_device(mpidrs) + .map_err(ArchVmError::SaveGic)?, + }) + } + + /// Restore the KVM VM state + /// + /// # Errors + /// + /// When [`crate::arch::aarch64::gic::GICDevice::restore_device`] errors. + pub fn restore_state(&mut self, mpidrs: &[u64], state: &VmState) -> Result<(), ArchVmError> { + self.get_irqchip() + .restore_device(mpidrs, &state.gic) + .map_err(ArchVmError::RestoreGic)?; + + Ok(()) + } +} + +/// Structure holding an general specific VM state. +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct VmState { + /// Guest memory state + pub memory: GuestMemoryState, + /// GIC state. + pub gic: GicState, +} diff --git a/src/vmm/src/arch/mod.rs b/src/vmm/src/arch/mod.rs index f5a2f98cb7c..61d65fea1a5 100644 --- a/src/vmm/src/arch/mod.rs +++ b/src/vmm/src/arch/mod.rs @@ -2,30 +2,47 @@ // SPDX-License-Identifier: Apache-2.0 use std::fmt; +use std::sync::LazyLock; +use log::warn; use serde::{Deserialize, Serialize}; +use vm_memory::GuestAddress; /// Module for aarch64 related functionality. #[cfg(target_arch = "aarch64")] pub mod aarch64; +#[cfg(target_arch = "aarch64")] +pub use aarch64::kvm::{Kvm, KvmArchError, OptionalCapabilities}; +#[cfg(target_arch = "aarch64")] +pub use aarch64::vcpu::*; +#[cfg(target_arch = "aarch64")] +pub use aarch64::vm::{ArchVm, ArchVmError, VmState}; #[cfg(target_arch = "aarch64")] pub use aarch64::{ - arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr, - layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, layout::SYSTEM_MEM_SIZE, - layout::SYSTEM_MEM_START, ConfigurationError, MMIO_MEM_SIZE, MMIO_MEM_START, + ConfigurationError, MMIO_MEM_SIZE, MMIO_MEM_START, arch_memory_regions, + configure_system_for_boot, get_kernel_start, initrd_load_addr, layout::CMDLINE_MAX_SIZE, + layout::IRQ_BASE, layout::IRQ_MAX, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, + load_kernel, }; /// Module for x86_64 related functionality. #[cfg(target_arch = "x86_64")] pub mod x86_64; +#[cfg(target_arch = "x86_64")] +pub use x86_64::kvm::{Kvm, KvmArchError}; +#[cfg(target_arch = "x86_64")] +pub use x86_64::vcpu::*; +#[cfg(target_arch = "x86_64")] +pub use x86_64::vm::{ArchVm, ArchVmError, VmState}; + #[cfg(target_arch = "x86_64")] pub use crate::arch::x86_64::{ - arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr, layout::APIC_ADDR, + ConfigurationError, MMIO_MEM_SIZE, MMIO_MEM_START, arch_memory_regions, + configure_system_for_boot, get_kernel_start, initrd_load_addr, layout::APIC_ADDR, layout::CMDLINE_MAX_SIZE, layout::IOAPIC_ADDR, layout::IRQ_BASE, layout::IRQ_MAX, - layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, ConfigurationError, MMIO_MEM_SIZE, - MMIO_MEM_START, + layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, load_kernel, }; /// Types of devices that can get attached to this platform. @@ -43,20 +60,57 @@ pub enum DeviceType { BootTimer, } -/// Type for passing information about the initrd in the guest memory. -#[derive(Debug)] -pub struct InitrdConfig { - /// Load address of initrd in guest memory - pub address: crate::vstate::memory::GuestAddress, - /// Size of initrd in guest memory - pub size: usize, -} +/// Default page size for the guest OS. +pub const GUEST_PAGE_SIZE: usize = 4096; -/// Default (smallest) memory page size for the supported architectures. -pub const PAGE_SIZE: usize = 4096; +/// Get the size of the host page size. +pub fn host_page_size() -> usize { + /// Default page size for the host OS. + static PAGE_SIZE: LazyLock = LazyLock::new(|| { + // # Safety: Value always valid + let r = unsafe { libc::sysconf(libc::_SC_PAGESIZE) }; + usize::try_from(r).unwrap_or_else(|_| { + warn!("Could not get host page size with sysconf, assuming default 4K host pages"); + 4096 + }) + }); + + *PAGE_SIZE +} impl fmt::Display for DeviceType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) } } + +/// Supported boot protocols for +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum BootProtocol { + /// Linux 64-bit boot protocol + LinuxBoot, + #[cfg(target_arch = "x86_64")] + /// PVH boot protocol (x86/HVM direct boot ABI) + PvhBoot, +} + +impl fmt::Display for BootProtocol { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match self { + BootProtocol::LinuxBoot => write!(f, "Linux 64-bit boot protocol"), + #[cfg(target_arch = "x86_64")] + BootProtocol::PvhBoot => write!(f, "PVH boot protocol"), + } + } +} + +#[derive(Debug, Copy, Clone)] +/// Specifies the entry point address where the guest must start +/// executing code, as well as which boot protocol is to be used +/// to configure the guest initial state. +pub struct EntryPoint { + /// Address in guest memory where the guest must start execution + pub entry_addr: GuestAddress, + /// Specifies which boot protocol to use + pub protocol: BootProtocol, +} diff --git a/src/vmm/src/arch/x86_64/cpu_model.rs b/src/vmm/src/arch/x86_64/cpu_model.rs index befaa3953bf..fd3399a736f 100644 --- a/src/vmm/src/arch/x86_64/cpu_model.rs +++ b/src/vmm/src/arch/x86_64/cpu_model.rs @@ -56,10 +56,10 @@ impl From<&u32> for CpuModel { impl From<&CpuModel> for u32 { fn from(cpu_model: &CpuModel) -> Self { - u32::from(cpu_model.extended_family) << 20 - | u32::from(cpu_model.extended_model) << 16 - | u32::from(cpu_model.family) << 8 - | u32::from(cpu_model.model) << 4 + (u32::from(cpu_model.extended_family) << 20) + | (u32::from(cpu_model.extended_model) << 16) + | (u32::from(cpu_model.family) << 8) + | (u32::from(cpu_model.model) << 4) | u32::from(cpu_model.stepping) } } diff --git a/src/vmm/src/arch/x86_64/gdt.rs b/src/vmm/src/arch/x86_64/gdt.rs index 41f2b0f8340..b7b99a572ae 100644 --- a/src/vmm/src/arch/x86_64/gdt.rs +++ b/src/vmm/src/arch/x86_64/gdt.rs @@ -1,3 +1,5 @@ +// Copyright © 2020, Oracle and/or its affiliates. +// // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // @@ -24,8 +26,38 @@ fn get_base(entry: u64) -> u64 { | (((entry) & 0x0000_0000_FFFF_0000) >> 16) } +// Extract the segment limit from the GDT segment descriptor. +// +// In a segment descriptor, the limit field is 20 bits, so it can directly describe +// a range from 0 to 0xFFFFF (1 MB). When G flag is set (4-KByte page granularity) it +// scales the value in the limit field by a factor of 2^12 (4 Kbytes), making the effective +// limit range from 0xFFF (4 KBytes) to 0xFFFF_FFFF (4 GBytes). +// +// However, the limit field in the VMCS definition is a 32 bit field, and the limit value is not +// automatically scaled using the G flag. This means that for a desired range of 4GB for a +// given segment, its limit must be specified as 0xFFFF_FFFF. Therefore the method of obtaining +// the limit from the GDT entry is not sufficient, since it only provides 20 bits when 32 bits +// are necessary. Fortunately, we can check if the G flag is set when extracting the limit since +// the full GDT entry is passed as an argument, and perform the scaling of the limit value to +// return the full 32 bit value. +// +// The scaling mentioned above is required when using PVH boot, since the guest boots in protected +// (32-bit) mode and must be able to access the entire 32-bit address space. It does not cause +// issues for the case of direct boot to 64-bit (long) mode, since in 64-bit mode the processor does +// not perform runtime limit checking on code or data segments. +// +// (For more information concerning the formats of segment descriptors, VMCS fields, et cetera, +// please consult the Intel Software Developer Manual.) fn get_limit(entry: u64) -> u32 { - ((((entry) & 0x000F_0000_0000_0000) >> 32) as u32) | (((entry) & 0x0000_0000_0000_FFFF) as u32) + #[allow(clippy::cast_possible_truncation)] // clearly, truncation is not possible + let limit: u32 = + ((((entry) & 0x000F_0000_0000_0000) >> 32) | ((entry) & 0x0000_0000_0000_FFFF)) as u32; + + // Perform manual limit scaling if G flag is set + match get_g(entry) { + 0 => limit, + _ => (limit << 12) | 0xFFF, // G flag is either 0 or 1 + } } fn get_g(entry: u64) -> u8 { @@ -109,7 +141,7 @@ mod tests { assert_eq!(0xB, seg.type_); // base and limit assert_eq!(0x10_0000, seg.base); - assert_eq!(0xfffff, seg.limit); + assert_eq!(0xffff_ffff, seg.limit); assert_eq!(0x0, seg.unusable); } } diff --git a/src/vmm/src/arch/x86_64/gen/mpspec.rs b/src/vmm/src/arch/x86_64/gen/mpspec.rs deleted file mode 100644 index 93103e5bee6..00000000000 --- a/src/vmm/src/arch/x86_64/gen/mpspec.rs +++ /dev/null @@ -1,825 +0,0 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// automatically generated by tools/bindgen.sh - -#![allow( - non_camel_case_types, - non_upper_case_globals, - dead_code, - non_snake_case, - clippy::ptr_as_ptr, - clippy::undocumented_unsafe_blocks, - missing_debug_implementations, - clippy::tests_outside_test_module -)] - -pub const MPC_SIGNATURE: &[u8; 5] = b"PCMP\0"; -pub const MP_PROCESSOR: u32 = 0; -pub const MP_BUS: u32 = 1; -pub const MP_IOAPIC: u32 = 2; -pub const MP_INTSRC: u32 = 3; -pub const MP_LINTSRC: u32 = 4; -pub const MP_TRANSLATION: u32 = 192; -pub const CPU_ENABLED: u32 = 1; -pub const CPU_BOOTPROCESSOR: u32 = 2; -pub const CPU_STEPPING_MASK: u32 = 15; -pub const CPU_MODEL_MASK: u32 = 240; -pub const CPU_FAMILY_MASK: u32 = 3840; -pub const BUSTYPE_EISA: &[u8; 5] = b"EISA\0"; -pub const BUSTYPE_ISA: &[u8; 4] = b"ISA\0"; -pub const BUSTYPE_INTERN: &[u8; 7] = b"INTERN\0"; -pub const BUSTYPE_MCA: &[u8; 4] = b"MCA\0"; -pub const BUSTYPE_VL: &[u8; 3] = b"VL\0"; -pub const BUSTYPE_PCI: &[u8; 4] = b"PCI\0"; -pub const BUSTYPE_PCMCIA: &[u8; 7] = b"PCMCIA\0"; -pub const BUSTYPE_CBUS: &[u8; 5] = b"CBUS\0"; -pub const BUSTYPE_CBUSII: &[u8; 7] = b"CBUSII\0"; -pub const BUSTYPE_FUTURE: &[u8; 7] = b"FUTURE\0"; -pub const BUSTYPE_MBI: &[u8; 4] = b"MBI\0"; -pub const BUSTYPE_MBII: &[u8; 5] = b"MBII\0"; -pub const BUSTYPE_MPI: &[u8; 4] = b"MPI\0"; -pub const BUSTYPE_MPSA: &[u8; 5] = b"MPSA\0"; -pub const BUSTYPE_NUBUS: &[u8; 6] = b"NUBUS\0"; -pub const BUSTYPE_TC: &[u8; 3] = b"TC\0"; -pub const BUSTYPE_VME: &[u8; 4] = b"VME\0"; -pub const BUSTYPE_XPRESS: &[u8; 7] = b"XPRESS\0"; -pub const MPC_APIC_USABLE: u32 = 1; -pub const MP_IRQPOL_DEFAULT: u32 = 0; -pub const MP_IRQPOL_ACTIVE_HIGH: u32 = 1; -pub const MP_IRQPOL_RESERVED: u32 = 2; -pub const MP_IRQPOL_ACTIVE_LOW: u32 = 3; -pub const MP_IRQPOL_MASK: u32 = 3; -pub const MP_IRQTRIG_DEFAULT: u32 = 0; -pub const MP_IRQTRIG_EDGE: u32 = 4; -pub const MP_IRQTRIG_RESERVED: u32 = 8; -pub const MP_IRQTRIG_LEVEL: u32 = 12; -pub const MP_IRQTRIG_MASK: u32 = 12; -pub const MP_APIC_ALL: u32 = 255; -pub const MPC_OEM_SIGNATURE: &[u8; 5] = b"_OEM\0"; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct mpf_intel { - pub signature: [::std::os::raw::c_char; 4usize], - pub physptr: ::std::os::raw::c_uint, - pub length: ::std::os::raw::c_uchar, - pub specification: ::std::os::raw::c_uchar, - pub checksum: ::std::os::raw::c_uchar, - pub feature1: ::std::os::raw::c_uchar, - pub feature2: ::std::os::raw::c_uchar, - pub feature3: ::std::os::raw::c_uchar, - pub feature4: ::std::os::raw::c_uchar, - pub feature5: ::std::os::raw::c_uchar, -} -#[test] -fn bindgen_test_layout_mpf_intel() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(mpf_intel)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(mpf_intel)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).signature) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(signature) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).physptr) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(physptr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).length) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(length) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).specification) as usize - ptr as usize }, - 9usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(specification) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).checksum) as usize - ptr as usize }, - 10usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(checksum) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).feature1) as usize - ptr as usize }, - 11usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(feature1) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).feature2) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(feature2) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).feature3) as usize - ptr as usize }, - 13usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(feature3) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).feature4) as usize - ptr as usize }, - 14usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(feature4) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).feature5) as usize - ptr as usize }, - 15usize, - concat!( - "Offset of field: ", - stringify!(mpf_intel), - "::", - stringify!(feature5) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct mpc_table { - pub signature: [::std::os::raw::c_char; 4usize], - pub length: ::std::os::raw::c_ushort, - pub spec: ::std::os::raw::c_char, - pub checksum: ::std::os::raw::c_char, - pub oem: [::std::os::raw::c_char; 8usize], - pub productid: [::std::os::raw::c_char; 12usize], - pub oemptr: ::std::os::raw::c_uint, - pub oemsize: ::std::os::raw::c_ushort, - pub oemcount: ::std::os::raw::c_ushort, - pub lapic: ::std::os::raw::c_uint, - pub reserved: ::std::os::raw::c_uint, -} -#[test] -fn bindgen_test_layout_mpc_table() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 44usize, - concat!("Size of: ", stringify!(mpc_table)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(mpc_table)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).signature) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(signature) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).length) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(length) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).spec) as usize - ptr as usize }, - 6usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(spec) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).checksum) as usize - ptr as usize }, - 7usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(checksum) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).oem) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(oem) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).productid) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(productid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).oemptr) as usize - ptr as usize }, - 28usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(oemptr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).oemsize) as usize - ptr as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(oemsize) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).oemcount) as usize - ptr as usize }, - 34usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(oemcount) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).lapic) as usize - ptr as usize }, - 36usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(lapic) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).reserved) as usize - ptr as usize }, - 40usize, - concat!( - "Offset of field: ", - stringify!(mpc_table), - "::", - stringify!(reserved) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct mpc_cpu { - pub type_: ::std::os::raw::c_uchar, - pub apicid: ::std::os::raw::c_uchar, - pub apicver: ::std::os::raw::c_uchar, - pub cpuflag: ::std::os::raw::c_uchar, - pub cpufeature: ::std::os::raw::c_uint, - pub featureflag: ::std::os::raw::c_uint, - pub reserved: [::std::os::raw::c_uint; 2usize], -} -#[test] -fn bindgen_test_layout_mpc_cpu() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 20usize, - concat!("Size of: ", stringify!(mpc_cpu)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(mpc_cpu)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).type_) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(mpc_cpu), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).apicid) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(mpc_cpu), - "::", - stringify!(apicid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).apicver) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(mpc_cpu), - "::", - stringify!(apicver) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).cpuflag) as usize - ptr as usize }, - 3usize, - concat!( - "Offset of field: ", - stringify!(mpc_cpu), - "::", - stringify!(cpuflag) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).cpufeature) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(mpc_cpu), - "::", - stringify!(cpufeature) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).featureflag) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(mpc_cpu), - "::", - stringify!(featureflag) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).reserved) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(mpc_cpu), - "::", - stringify!(reserved) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct mpc_bus { - pub type_: ::std::os::raw::c_uchar, - pub busid: ::std::os::raw::c_uchar, - pub bustype: [::std::os::raw::c_uchar; 6usize], -} -#[test] -fn bindgen_test_layout_mpc_bus() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(mpc_bus)) - ); - assert_eq!( - ::std::mem::align_of::(), - 1usize, - concat!("Alignment of ", stringify!(mpc_bus)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).type_) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(mpc_bus), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).busid) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(mpc_bus), - "::", - stringify!(busid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).bustype) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(mpc_bus), - "::", - stringify!(bustype) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct mpc_ioapic { - pub type_: ::std::os::raw::c_uchar, - pub apicid: ::std::os::raw::c_uchar, - pub apicver: ::std::os::raw::c_uchar, - pub flags: ::std::os::raw::c_uchar, - pub apicaddr: ::std::os::raw::c_uint, -} -#[test] -fn bindgen_test_layout_mpc_ioapic() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(mpc_ioapic)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(mpc_ioapic)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).type_) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(mpc_ioapic), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).apicid) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(mpc_ioapic), - "::", - stringify!(apicid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).apicver) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(mpc_ioapic), - "::", - stringify!(apicver) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, - 3usize, - concat!( - "Offset of field: ", - stringify!(mpc_ioapic), - "::", - stringify!(flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).apicaddr) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(mpc_ioapic), - "::", - stringify!(apicaddr) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct mpc_intsrc { - pub type_: ::std::os::raw::c_uchar, - pub irqtype: ::std::os::raw::c_uchar, - pub irqflag: ::std::os::raw::c_ushort, - pub srcbus: ::std::os::raw::c_uchar, - pub srcbusirq: ::std::os::raw::c_uchar, - pub dstapic: ::std::os::raw::c_uchar, - pub dstirq: ::std::os::raw::c_uchar, -} -#[test] -fn bindgen_test_layout_mpc_intsrc() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(mpc_intsrc)) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!("Alignment of ", stringify!(mpc_intsrc)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).type_) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(mpc_intsrc), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).irqtype) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(mpc_intsrc), - "::", - stringify!(irqtype) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).irqflag) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(mpc_intsrc), - "::", - stringify!(irqflag) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).srcbus) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(mpc_intsrc), - "::", - stringify!(srcbus) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).srcbusirq) as usize - ptr as usize }, - 5usize, - concat!( - "Offset of field: ", - stringify!(mpc_intsrc), - "::", - stringify!(srcbusirq) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dstapic) as usize - ptr as usize }, - 6usize, - concat!( - "Offset of field: ", - stringify!(mpc_intsrc), - "::", - stringify!(dstapic) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dstirq) as usize - ptr as usize }, - 7usize, - concat!( - "Offset of field: ", - stringify!(mpc_intsrc), - "::", - stringify!(dstirq) - ) - ); -} -pub const mp_irq_source_types_mp_INT: mp_irq_source_types = 0; -pub const mp_irq_source_types_mp_NMI: mp_irq_source_types = 1; -pub const mp_irq_source_types_mp_SMI: mp_irq_source_types = 2; -pub const mp_irq_source_types_mp_ExtINT: mp_irq_source_types = 3; -pub type mp_irq_source_types = ::std::os::raw::c_uint; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct mpc_lintsrc { - pub type_: ::std::os::raw::c_uchar, - pub irqtype: ::std::os::raw::c_uchar, - pub irqflag: ::std::os::raw::c_ushort, - pub srcbusid: ::std::os::raw::c_uchar, - pub srcbusirq: ::std::os::raw::c_uchar, - pub destapic: ::std::os::raw::c_uchar, - pub destapiclint: ::std::os::raw::c_uchar, -} -#[test] -fn bindgen_test_layout_mpc_lintsrc() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(mpc_lintsrc)) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!("Alignment of ", stringify!(mpc_lintsrc)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).type_) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(mpc_lintsrc), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).irqtype) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(mpc_lintsrc), - "::", - stringify!(irqtype) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).irqflag) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(mpc_lintsrc), - "::", - stringify!(irqflag) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).srcbusid) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(mpc_lintsrc), - "::", - stringify!(srcbusid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).srcbusirq) as usize - ptr as usize }, - 5usize, - concat!( - "Offset of field: ", - stringify!(mpc_lintsrc), - "::", - stringify!(srcbusirq) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).destapic) as usize - ptr as usize }, - 6usize, - concat!( - "Offset of field: ", - stringify!(mpc_lintsrc), - "::", - stringify!(destapic) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).destapiclint) as usize - ptr as usize }, - 7usize, - concat!( - "Offset of field: ", - stringify!(mpc_lintsrc), - "::", - stringify!(destapiclint) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct mpc_oemtable { - pub signature: [::std::os::raw::c_char; 4usize], - pub length: ::std::os::raw::c_ushort, - pub rev: ::std::os::raw::c_char, - pub checksum: ::std::os::raw::c_char, - pub mpc: [::std::os::raw::c_char; 8usize], -} -#[test] -fn bindgen_test_layout_mpc_oemtable() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(mpc_oemtable)) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!("Alignment of ", stringify!(mpc_oemtable)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).signature) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(mpc_oemtable), - "::", - stringify!(signature) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).length) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(mpc_oemtable), - "::", - stringify!(length) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).rev) as usize - ptr as usize }, - 6usize, - concat!( - "Offset of field: ", - stringify!(mpc_oemtable), - "::", - stringify!(rev) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).checksum) as usize - ptr as usize }, - 7usize, - concat!( - "Offset of field: ", - stringify!(mpc_oemtable), - "::", - stringify!(checksum) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).mpc) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(mpc_oemtable), - "::", - stringify!(mpc) - ) - ); -} -pub const mp_bustype_MP_BUS_ISA: mp_bustype = 1; -pub const mp_bustype_MP_BUS_EISA: mp_bustype = 2; -pub const mp_bustype_MP_BUS_PCI: mp_bustype = 3; -pub type mp_bustype = ::std::os::raw::c_uint; diff --git a/src/vmm/src/arch/x86_64/generated/arch_prctl.rs b/src/vmm/src/arch/x86_64/generated/arch_prctl.rs new file mode 100644 index 00000000000..44f68883141 --- /dev/null +++ b/src/vmm/src/arch/x86_64/generated/arch_prctl.rs @@ -0,0 +1,44 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// automatically generated by tools/bindgen.sh + +#![allow( + non_camel_case_types, + non_upper_case_globals, + dead_code, + non_snake_case, + clippy::ptr_as_ptr, + clippy::undocumented_unsafe_blocks, + missing_debug_implementations, + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn +)] + +pub const ARCH_SET_GS: u32 = 4097; +pub const ARCH_SET_FS: u32 = 4098; +pub const ARCH_GET_FS: u32 = 4099; +pub const ARCH_GET_GS: u32 = 4100; +pub const ARCH_GET_CPUID: u32 = 4113; +pub const ARCH_SET_CPUID: u32 = 4114; +pub const ARCH_GET_XCOMP_SUPP: u32 = 4129; +pub const ARCH_GET_XCOMP_PERM: u32 = 4130; +pub const ARCH_REQ_XCOMP_PERM: u32 = 4131; +pub const ARCH_GET_XCOMP_GUEST_PERM: u32 = 4132; +pub const ARCH_REQ_XCOMP_GUEST_PERM: u32 = 4133; +pub const ARCH_XCOMP_TILECFG: u32 = 17; +pub const ARCH_XCOMP_TILEDATA: u32 = 18; +pub const ARCH_MAP_VDSO_X32: u32 = 8193; +pub const ARCH_MAP_VDSO_32: u32 = 8194; +pub const ARCH_MAP_VDSO_64: u32 = 8195; +pub const ARCH_GET_UNTAG_MASK: u32 = 16385; +pub const ARCH_ENABLE_TAGGED_ADDR: u32 = 16386; +pub const ARCH_GET_MAX_TAG_BITS: u32 = 16387; +pub const ARCH_FORCE_TAGGED_SVA: u32 = 16388; +pub const ARCH_SHSTK_ENABLE: u32 = 20481; +pub const ARCH_SHSTK_DISABLE: u32 = 20482; +pub const ARCH_SHSTK_LOCK: u32 = 20483; +pub const ARCH_SHSTK_UNLOCK: u32 = 20484; +pub const ARCH_SHSTK_STATUS: u32 = 20485; +pub const ARCH_SHSTK_SHSTK: u32 = 1; +pub const ARCH_SHSTK_WRSS: u32 = 2; diff --git a/src/vmm/src/arch/x86_64/gen/hyperv.rs b/src/vmm/src/arch/x86_64/generated/hyperv.rs similarity index 83% rename from src/vmm/src/arch/x86_64/gen/hyperv.rs rename to src/vmm/src/arch/x86_64/generated/hyperv.rs index 02ccc3adbcc..49dabeaca92 100644 --- a/src/vmm/src/arch/x86_64/gen/hyperv.rs +++ b/src/vmm/src/arch/x86_64/generated/hyperv.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,7 +11,8 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] pub const HV_X64_MSR_SYNDBG_CONTROL: u32 = 0x400000f1; diff --git a/src/vmm/src/arch/x86_64/gen/hyperv_tlfs.rs b/src/vmm/src/arch/x86_64/generated/hyperv_tlfs.rs similarity index 96% rename from src/vmm/src/arch/x86_64/gen/hyperv_tlfs.rs rename to src/vmm/src/arch/x86_64/generated/hyperv_tlfs.rs index 84b770a5506..fae4d497142 100644 --- a/src/vmm/src/arch/x86_64/gen/hyperv_tlfs.rs +++ b/src/vmm/src/arch/x86_64/generated/hyperv_tlfs.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,7 +11,8 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] pub const HV_X64_MSR_GUEST_OS_ID: u32 = 0x40000000; diff --git a/src/vmm/src/arch/x86_64/gen/mod.rs b/src/vmm/src/arch/x86_64/generated/mod.rs similarity index 95% rename from src/vmm/src/arch/x86_64/gen/mod.rs rename to src/vmm/src/arch/x86_64/generated/mod.rs index 35c2ca225e8..bded9e51455 100644 --- a/src/vmm/src/arch/x86_64/gen/mod.rs +++ b/src/vmm/src/arch/x86_64/generated/mod.rs @@ -5,9 +5,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. +pub mod arch_prctl; pub mod hyperv; pub mod hyperv_tlfs; pub mod mpspec; - pub mod msr_index; pub mod perf_event; diff --git a/src/vmm/src/arch/x86_64/generated/mpspec.rs b/src/vmm/src/arch/x86_64/generated/mpspec.rs new file mode 100644 index 00000000000..38252548367 --- /dev/null +++ b/src/vmm/src/arch/x86_64/generated/mpspec.rs @@ -0,0 +1,267 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// automatically generated by tools/bindgen.sh + +#![allow( + non_camel_case_types, + non_upper_case_globals, + dead_code, + non_snake_case, + clippy::ptr_as_ptr, + clippy::undocumented_unsafe_blocks, + missing_debug_implementations, + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn +)] + +pub const MPC_SIGNATURE: &[u8; 5] = b"PCMP\0"; +pub const MP_PROCESSOR: u32 = 0; +pub const MP_BUS: u32 = 1; +pub const MP_IOAPIC: u32 = 2; +pub const MP_INTSRC: u32 = 3; +pub const MP_LINTSRC: u32 = 4; +pub const MP_TRANSLATION: u32 = 192; +pub const CPU_ENABLED: u32 = 1; +pub const CPU_BOOTPROCESSOR: u32 = 2; +pub const CPU_STEPPING_MASK: u32 = 15; +pub const CPU_MODEL_MASK: u32 = 240; +pub const CPU_FAMILY_MASK: u32 = 3840; +pub const BUSTYPE_EISA: &[u8; 5] = b"EISA\0"; +pub const BUSTYPE_ISA: &[u8; 4] = b"ISA\0"; +pub const BUSTYPE_INTERN: &[u8; 7] = b"INTERN\0"; +pub const BUSTYPE_MCA: &[u8; 4] = b"MCA\0"; +pub const BUSTYPE_VL: &[u8; 3] = b"VL\0"; +pub const BUSTYPE_PCI: &[u8; 4] = b"PCI\0"; +pub const BUSTYPE_PCMCIA: &[u8; 7] = b"PCMCIA\0"; +pub const BUSTYPE_CBUS: &[u8; 5] = b"CBUS\0"; +pub const BUSTYPE_CBUSII: &[u8; 7] = b"CBUSII\0"; +pub const BUSTYPE_FUTURE: &[u8; 7] = b"FUTURE\0"; +pub const BUSTYPE_MBI: &[u8; 4] = b"MBI\0"; +pub const BUSTYPE_MBII: &[u8; 5] = b"MBII\0"; +pub const BUSTYPE_MPI: &[u8; 4] = b"MPI\0"; +pub const BUSTYPE_MPSA: &[u8; 5] = b"MPSA\0"; +pub const BUSTYPE_NUBUS: &[u8; 6] = b"NUBUS\0"; +pub const BUSTYPE_TC: &[u8; 3] = b"TC\0"; +pub const BUSTYPE_VME: &[u8; 4] = b"VME\0"; +pub const BUSTYPE_XPRESS: &[u8; 7] = b"XPRESS\0"; +pub const MPC_APIC_USABLE: u32 = 1; +pub const MP_IRQPOL_DEFAULT: u32 = 0; +pub const MP_IRQPOL_ACTIVE_HIGH: u32 = 1; +pub const MP_IRQPOL_RESERVED: u32 = 2; +pub const MP_IRQPOL_ACTIVE_LOW: u32 = 3; +pub const MP_IRQPOL_MASK: u32 = 3; +pub const MP_IRQTRIG_DEFAULT: u32 = 0; +pub const MP_IRQTRIG_EDGE: u32 = 4; +pub const MP_IRQTRIG_RESERVED: u32 = 8; +pub const MP_IRQTRIG_LEVEL: u32 = 12; +pub const MP_IRQTRIG_MASK: u32 = 12; +pub const MP_APIC_ALL: u32 = 255; +pub const MPC_OEM_SIGNATURE: &[u8; 5] = b"_OEM\0"; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct mpf_intel { + pub signature: [::std::os::raw::c_char; 4usize], + pub physptr: ::std::os::raw::c_uint, + pub length: ::std::os::raw::c_uchar, + pub specification: ::std::os::raw::c_uchar, + pub checksum: ::std::os::raw::c_uchar, + pub feature1: ::std::os::raw::c_uchar, + pub feature2: ::std::os::raw::c_uchar, + pub feature3: ::std::os::raw::c_uchar, + pub feature4: ::std::os::raw::c_uchar, + pub feature5: ::std::os::raw::c_uchar, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mpf_intel"][::std::mem::size_of::() - 16usize]; + ["Alignment of mpf_intel"][::std::mem::align_of::() - 4usize]; + ["Offset of field: mpf_intel::signature"] + [::std::mem::offset_of!(mpf_intel, signature) - 0usize]; + ["Offset of field: mpf_intel::physptr"][::std::mem::offset_of!(mpf_intel, physptr) - 4usize]; + ["Offset of field: mpf_intel::length"][::std::mem::offset_of!(mpf_intel, length) - 8usize]; + ["Offset of field: mpf_intel::specification"] + [::std::mem::offset_of!(mpf_intel, specification) - 9usize]; + ["Offset of field: mpf_intel::checksum"][::std::mem::offset_of!(mpf_intel, checksum) - 10usize]; + ["Offset of field: mpf_intel::feature1"][::std::mem::offset_of!(mpf_intel, feature1) - 11usize]; + ["Offset of field: mpf_intel::feature2"][::std::mem::offset_of!(mpf_intel, feature2) - 12usize]; + ["Offset of field: mpf_intel::feature3"][::std::mem::offset_of!(mpf_intel, feature3) - 13usize]; + ["Offset of field: mpf_intel::feature4"][::std::mem::offset_of!(mpf_intel, feature4) - 14usize]; + ["Offset of field: mpf_intel::feature5"][::std::mem::offset_of!(mpf_intel, feature5) - 15usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct mpc_table { + pub signature: [::std::os::raw::c_char; 4usize], + pub length: ::std::os::raw::c_ushort, + pub spec: ::std::os::raw::c_char, + pub checksum: ::std::os::raw::c_char, + pub oem: [::std::os::raw::c_char; 8usize], + pub productid: [::std::os::raw::c_char; 12usize], + pub oemptr: ::std::os::raw::c_uint, + pub oemsize: ::std::os::raw::c_ushort, + pub oemcount: ::std::os::raw::c_ushort, + pub lapic: ::std::os::raw::c_uint, + pub reserved: ::std::os::raw::c_uint, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mpc_table"][::std::mem::size_of::() - 44usize]; + ["Alignment of mpc_table"][::std::mem::align_of::() - 4usize]; + ["Offset of field: mpc_table::signature"] + [::std::mem::offset_of!(mpc_table, signature) - 0usize]; + ["Offset of field: mpc_table::length"][::std::mem::offset_of!(mpc_table, length) - 4usize]; + ["Offset of field: mpc_table::spec"][::std::mem::offset_of!(mpc_table, spec) - 6usize]; + ["Offset of field: mpc_table::checksum"][::std::mem::offset_of!(mpc_table, checksum) - 7usize]; + ["Offset of field: mpc_table::oem"][::std::mem::offset_of!(mpc_table, oem) - 8usize]; + ["Offset of field: mpc_table::productid"] + [::std::mem::offset_of!(mpc_table, productid) - 16usize]; + ["Offset of field: mpc_table::oemptr"][::std::mem::offset_of!(mpc_table, oemptr) - 28usize]; + ["Offset of field: mpc_table::oemsize"][::std::mem::offset_of!(mpc_table, oemsize) - 32usize]; + ["Offset of field: mpc_table::oemcount"][::std::mem::offset_of!(mpc_table, oemcount) - 34usize]; + ["Offset of field: mpc_table::lapic"][::std::mem::offset_of!(mpc_table, lapic) - 36usize]; + ["Offset of field: mpc_table::reserved"][::std::mem::offset_of!(mpc_table, reserved) - 40usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct mpc_cpu { + pub type_: ::std::os::raw::c_uchar, + pub apicid: ::std::os::raw::c_uchar, + pub apicver: ::std::os::raw::c_uchar, + pub cpuflag: ::std::os::raw::c_uchar, + pub cpufeature: ::std::os::raw::c_uint, + pub featureflag: ::std::os::raw::c_uint, + pub reserved: [::std::os::raw::c_uint; 2usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mpc_cpu"][::std::mem::size_of::() - 20usize]; + ["Alignment of mpc_cpu"][::std::mem::align_of::() - 4usize]; + ["Offset of field: mpc_cpu::type_"][::std::mem::offset_of!(mpc_cpu, type_) - 0usize]; + ["Offset of field: mpc_cpu::apicid"][::std::mem::offset_of!(mpc_cpu, apicid) - 1usize]; + ["Offset of field: mpc_cpu::apicver"][::std::mem::offset_of!(mpc_cpu, apicver) - 2usize]; + ["Offset of field: mpc_cpu::cpuflag"][::std::mem::offset_of!(mpc_cpu, cpuflag) - 3usize]; + ["Offset of field: mpc_cpu::cpufeature"][::std::mem::offset_of!(mpc_cpu, cpufeature) - 4usize]; + ["Offset of field: mpc_cpu::featureflag"] + [::std::mem::offset_of!(mpc_cpu, featureflag) - 8usize]; + ["Offset of field: mpc_cpu::reserved"][::std::mem::offset_of!(mpc_cpu, reserved) - 12usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct mpc_bus { + pub type_: ::std::os::raw::c_uchar, + pub busid: ::std::os::raw::c_uchar, + pub bustype: [::std::os::raw::c_uchar; 6usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mpc_bus"][::std::mem::size_of::() - 8usize]; + ["Alignment of mpc_bus"][::std::mem::align_of::() - 1usize]; + ["Offset of field: mpc_bus::type_"][::std::mem::offset_of!(mpc_bus, type_) - 0usize]; + ["Offset of field: mpc_bus::busid"][::std::mem::offset_of!(mpc_bus, busid) - 1usize]; + ["Offset of field: mpc_bus::bustype"][::std::mem::offset_of!(mpc_bus, bustype) - 2usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct mpc_ioapic { + pub type_: ::std::os::raw::c_uchar, + pub apicid: ::std::os::raw::c_uchar, + pub apicver: ::std::os::raw::c_uchar, + pub flags: ::std::os::raw::c_uchar, + pub apicaddr: ::std::os::raw::c_uint, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mpc_ioapic"][::std::mem::size_of::() - 8usize]; + ["Alignment of mpc_ioapic"][::std::mem::align_of::() - 4usize]; + ["Offset of field: mpc_ioapic::type_"][::std::mem::offset_of!(mpc_ioapic, type_) - 0usize]; + ["Offset of field: mpc_ioapic::apicid"][::std::mem::offset_of!(mpc_ioapic, apicid) - 1usize]; + ["Offset of field: mpc_ioapic::apicver"][::std::mem::offset_of!(mpc_ioapic, apicver) - 2usize]; + ["Offset of field: mpc_ioapic::flags"][::std::mem::offset_of!(mpc_ioapic, flags) - 3usize]; + ["Offset of field: mpc_ioapic::apicaddr"] + [::std::mem::offset_of!(mpc_ioapic, apicaddr) - 4usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct mpc_intsrc { + pub type_: ::std::os::raw::c_uchar, + pub irqtype: ::std::os::raw::c_uchar, + pub irqflag: ::std::os::raw::c_ushort, + pub srcbus: ::std::os::raw::c_uchar, + pub srcbusirq: ::std::os::raw::c_uchar, + pub dstapic: ::std::os::raw::c_uchar, + pub dstirq: ::std::os::raw::c_uchar, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mpc_intsrc"][::std::mem::size_of::() - 8usize]; + ["Alignment of mpc_intsrc"][::std::mem::align_of::() - 2usize]; + ["Offset of field: mpc_intsrc::type_"][::std::mem::offset_of!(mpc_intsrc, type_) - 0usize]; + ["Offset of field: mpc_intsrc::irqtype"][::std::mem::offset_of!(mpc_intsrc, irqtype) - 1usize]; + ["Offset of field: mpc_intsrc::irqflag"][::std::mem::offset_of!(mpc_intsrc, irqflag) - 2usize]; + ["Offset of field: mpc_intsrc::srcbus"][::std::mem::offset_of!(mpc_intsrc, srcbus) - 4usize]; + ["Offset of field: mpc_intsrc::srcbusirq"] + [::std::mem::offset_of!(mpc_intsrc, srcbusirq) - 5usize]; + ["Offset of field: mpc_intsrc::dstapic"][::std::mem::offset_of!(mpc_intsrc, dstapic) - 6usize]; + ["Offset of field: mpc_intsrc::dstirq"][::std::mem::offset_of!(mpc_intsrc, dstirq) - 7usize]; +}; +pub const mp_irq_source_types_mp_INT: mp_irq_source_types = 0; +pub const mp_irq_source_types_mp_NMI: mp_irq_source_types = 1; +pub const mp_irq_source_types_mp_SMI: mp_irq_source_types = 2; +pub const mp_irq_source_types_mp_ExtINT: mp_irq_source_types = 3; +pub type mp_irq_source_types = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct mpc_lintsrc { + pub type_: ::std::os::raw::c_uchar, + pub irqtype: ::std::os::raw::c_uchar, + pub irqflag: ::std::os::raw::c_ushort, + pub srcbusid: ::std::os::raw::c_uchar, + pub srcbusirq: ::std::os::raw::c_uchar, + pub destapic: ::std::os::raw::c_uchar, + pub destapiclint: ::std::os::raw::c_uchar, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mpc_lintsrc"][::std::mem::size_of::() - 8usize]; + ["Alignment of mpc_lintsrc"][::std::mem::align_of::() - 2usize]; + ["Offset of field: mpc_lintsrc::type_"][::std::mem::offset_of!(mpc_lintsrc, type_) - 0usize]; + ["Offset of field: mpc_lintsrc::irqtype"] + [::std::mem::offset_of!(mpc_lintsrc, irqtype) - 1usize]; + ["Offset of field: mpc_lintsrc::irqflag"] + [::std::mem::offset_of!(mpc_lintsrc, irqflag) - 2usize]; + ["Offset of field: mpc_lintsrc::srcbusid"] + [::std::mem::offset_of!(mpc_lintsrc, srcbusid) - 4usize]; + ["Offset of field: mpc_lintsrc::srcbusirq"] + [::std::mem::offset_of!(mpc_lintsrc, srcbusirq) - 5usize]; + ["Offset of field: mpc_lintsrc::destapic"] + [::std::mem::offset_of!(mpc_lintsrc, destapic) - 6usize]; + ["Offset of field: mpc_lintsrc::destapiclint"] + [::std::mem::offset_of!(mpc_lintsrc, destapiclint) - 7usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct mpc_oemtable { + pub signature: [::std::os::raw::c_char; 4usize], + pub length: ::std::os::raw::c_ushort, + pub rev: ::std::os::raw::c_char, + pub checksum: ::std::os::raw::c_char, + pub mpc: [::std::os::raw::c_char; 8usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of mpc_oemtable"][::std::mem::size_of::() - 16usize]; + ["Alignment of mpc_oemtable"][::std::mem::align_of::() - 2usize]; + ["Offset of field: mpc_oemtable::signature"] + [::std::mem::offset_of!(mpc_oemtable, signature) - 0usize]; + ["Offset of field: mpc_oemtable::length"] + [::std::mem::offset_of!(mpc_oemtable, length) - 4usize]; + ["Offset of field: mpc_oemtable::rev"][::std::mem::offset_of!(mpc_oemtable, rev) - 6usize]; + ["Offset of field: mpc_oemtable::checksum"] + [::std::mem::offset_of!(mpc_oemtable, checksum) - 7usize]; + ["Offset of field: mpc_oemtable::mpc"][::std::mem::offset_of!(mpc_oemtable, mpc) - 8usize]; +}; +pub const mp_bustype_MP_BUS_ISA: mp_bustype = 1; +pub const mp_bustype_MP_BUS_EISA: mp_bustype = 2; +pub const mp_bustype_MP_BUS_PCI: mp_bustype = 3; +pub type mp_bustype = ::std::os::raw::c_uint; diff --git a/src/vmm/src/arch/x86_64/gen/msr_index.rs b/src/vmm/src/arch/x86_64/generated/msr_index.rs similarity index 99% rename from src/vmm/src/arch/x86_64/gen/msr_index.rs rename to src/vmm/src/arch/x86_64/generated/msr_index.rs index 0acc88b6fd1..041bffe7ec3 100644 --- a/src/vmm/src/arch/x86_64/gen/msr_index.rs +++ b/src/vmm/src/arch/x86_64/generated/msr_index.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,7 +11,8 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] pub const MSR_EFER: u32 = 0xc0000080; diff --git a/src/vmm/src/arch/x86_64/gen/perf_event.rs b/src/vmm/src/arch/x86_64/generated/perf_event.rs similarity index 85% rename from src/vmm/src/arch/x86_64/gen/perf_event.rs rename to src/vmm/src/arch/x86_64/generated/perf_event.rs index f00951e0c19..66b851e3451 100644 --- a/src/vmm/src/arch/x86_64/gen/perf_event.rs +++ b/src/vmm/src/arch/x86_64/generated/perf_event.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,7 +11,8 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] pub const MSR_ARCH_PERFMON_PERFCTR0: u32 = 0xc1; diff --git a/src/vmm/src/arch/x86_64/interrupts.rs b/src/vmm/src/arch/x86_64/interrupts.rs index 8b7bf8bc793..8deb5a9d44e 100644 --- a/src/vmm/src/arch/x86_64/interrupts.rs +++ b/src/vmm/src/arch/x86_64/interrupts.rs @@ -7,6 +7,7 @@ use kvm_bindings::kvm_lapic_state; use kvm_ioctls::VcpuFd; +use zerocopy::IntoBytes; use crate::utils::byte_order; @@ -28,13 +29,13 @@ const APIC_MODE_EXTINT: u32 = 0x7; fn get_klapic_reg(klapic: &kvm_lapic_state, reg_offset: usize) -> u32 { let range = reg_offset..reg_offset + 4; let reg = klapic.regs.get(range).expect("get_klapic_reg range"); - byte_order::read_le_u32_from_i8(reg) + byte_order::read_le_u32(reg.as_bytes()) } fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) { let range = reg_offset..reg_offset + 4; let reg = klapic.regs.get_mut(range).expect("set_klapic_reg range"); - byte_order::write_le_u32_to_i8(reg, value) + byte_order::write_le_u32(reg.as_mut_bytes(), value); } fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 { diff --git a/src/vmm/src/arch/x86_64/kvm.rs b/src/vmm/src/arch/x86_64/kvm.rs new file mode 100644 index 00000000000..bad7f134d58 --- /dev/null +++ b/src/vmm/src/arch/x86_64/kvm.rs @@ -0,0 +1,70 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use kvm_bindings::{CpuId, KVM_MAX_CPUID_ENTRIES, MsrList}; +use kvm_ioctls::Kvm as KvmFd; + +use crate::arch::x86_64::xstate::{XstateError, request_dynamic_xstate_features}; +use crate::cpu_config::templates::KvmCapability; + +/// Architecture specific error for KVM initialization +#[derive(Debug, thiserror::Error, displaydoc::Display)] +pub enum KvmArchError { + /// Failed to get supported cpuid: {0} + GetSupportedCpuId(kvm_ioctls::Error), + /// Failed to request permission for dynamic XSTATE features: {0} + XstateFeatures(XstateError), +} + +/// Struct with kvm fd and kvm associated parameters. +#[derive(Debug)] +pub struct Kvm { + /// KVM fd. + pub fd: KvmFd, + /// Additional capabilities that were specified in cpu template. + pub kvm_cap_modifiers: Vec, + /// Supported CpuIds. + pub supported_cpuid: CpuId, +} + +impl Kvm { + pub(crate) const DEFAULT_CAPABILITIES: [u32; 14] = [ + kvm_bindings::KVM_CAP_IRQCHIP, + kvm_bindings::KVM_CAP_IOEVENTFD, + kvm_bindings::KVM_CAP_IRQFD, + kvm_bindings::KVM_CAP_USER_MEMORY, + kvm_bindings::KVM_CAP_SET_TSS_ADDR, + kvm_bindings::KVM_CAP_PIT2, + kvm_bindings::KVM_CAP_PIT_STATE2, + kvm_bindings::KVM_CAP_ADJUST_CLOCK, + kvm_bindings::KVM_CAP_DEBUGREGS, + kvm_bindings::KVM_CAP_MP_STATE, + kvm_bindings::KVM_CAP_VCPU_EVENTS, + kvm_bindings::KVM_CAP_XCRS, + kvm_bindings::KVM_CAP_XSAVE, + kvm_bindings::KVM_CAP_EXT_CPUID, + ]; + + /// Initialize [`Kvm`] type for x86_64 architecture + pub fn init_arch( + fd: KvmFd, + kvm_cap_modifiers: Vec, + ) -> Result { + request_dynamic_xstate_features().map_err(KvmArchError::XstateFeatures)?; + + let supported_cpuid = fd + .get_supported_cpuid(KVM_MAX_CPUID_ENTRIES) + .map_err(KvmArchError::GetSupportedCpuId)?; + + Ok(Kvm { + fd, + kvm_cap_modifiers, + supported_cpuid, + }) + } + + /// Msrs needed to be saved on snapshot creation. + pub fn msrs_to_save(&self) -> Result { + crate::arch::x86_64::msr::get_msrs_to_save(&self.fd) + } +} diff --git a/src/vmm/src/arch/x86_64/layout.rs b/src/vmm/src/arch/x86_64/layout.rs index 01355b3018a..18d718a49b8 100644 --- a/src/vmm/src/arch/x86_64/layout.rs +++ b/src/vmm/src/arch/x86_64/layout.rs @@ -5,7 +5,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -/// Magic addresses externally used to lay out x86_64 VMs. +//! Magic addresses externally used to lay out x86_64 VMs. /// Initial stack for the boot CPU. pub const BOOT_STACK_POINTER: u64 = 0x8ff0; @@ -27,6 +27,17 @@ pub const IRQ_MAX: u32 = 23; /// Address for the TSS setup. pub const KVM_TSS_ADDRESS: u64 = 0xfffb_d000; +/// Address of the hvm_start_info struct used in PVH boot +pub const PVH_INFO_START: u64 = 0x6000; + +/// Starting address of array of modules of hvm_modlist_entry type. +/// Used to enable initrd support using the PVH boot ABI. +pub const MODLIST_START: u64 = 0x6040; + +/// Address of memory map table used in PVH boot. Can overlap +/// with the zero page address since they are mutually exclusive. +pub const MEMMAP_START: u64 = 0x7000; + /// The 'zero page', a.k.a linux kernel bootparams. pub const ZERO_PAGE_START: u64 = 0x7000; diff --git a/src/vmm/src/arch/x86_64/mod.rs b/src/vmm/src/arch/x86_64/mod.rs index 1066d9734c3..ca350cbf9af 100644 --- a/src/vmm/src/arch/x86_64/mod.rs +++ b/src/vmm/src/arch/x86_64/mod.rs @@ -1,3 +1,5 @@ +// Copyright © 2020, Oracle and/or its affiliates. +// // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // @@ -10,6 +12,8 @@ pub mod cpu_model; mod gdt; /// Contains logic for setting up Advanced Programmable Interrupt Controller (local version). pub mod interrupts; +/// Architecture specific KVM-related code +pub mod kvm; /// Layout for the x86_64 system. pub mod layout; mod mptable; @@ -17,29 +21,54 @@ mod mptable; pub mod msr; /// Logic for configuring x86_64 registers. pub mod regs; +/// Architecture specific vCPU code +pub mod vcpu; +/// Architecture specific VM state code +pub mod vm; +/// Logic for configuring XSTATE features. +pub mod xstate; #[allow(missing_docs)] -pub mod gen; +pub mod generated; + +use std::fs::File; +use layout::CMDLINE_START; use linux_loader::configurator::linux::LinuxBootConfigurator; +use linux_loader::configurator::pvh::PvhBootConfigurator; use linux_loader::configurator::{BootConfigurator, BootParams}; use linux_loader::loader::bootparam::boot_params; - -use crate::arch::InitrdConfig; -use crate::device_manager::resources::ResourceAllocator; -use crate::utils::u64_to_usize; +use linux_loader::loader::elf::Elf as Loader; +use linux_loader::loader::elf::start_info::{ + hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info, +}; +use linux_loader::loader::{Cmdline, KernelLoader, PvhBootCapability, load_cmdline}; +use log::debug; + +use super::EntryPoint; +use crate::acpi::create_acpi_tables; +use crate::arch::{BootProtocol, SYSTEM_MEM_SIZE, SYSTEM_MEM_START}; +use crate::cpu_config::templates::{CustomCpuTemplate, GuestConfigError}; +use crate::cpu_config::x86_64::CpuConfiguration; +use crate::initrd::InitrdConfig; +use crate::utils::{align_down, mib_to_bytes, u64_to_usize, usize_to_u64}; +use crate::vmm_config::machine_config::MachineConfig; use crate::vstate::memory::{ Address, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, }; +use crate::vstate::vcpu::KvmVcpuConfigureError; +use crate::{Vcpu, VcpuConfig, Vmm}; // Value taken from https://elixir.bootlin.com/linux/v5.10.68/source/arch/x86/include/uapi/asm/e820.h#L31 // Usable normal RAM const E820_RAM: u32 = 1; + // Reserved area that should be avoided during memory allocations const E820_RESERVED: u32 = 2; +const MEMMAP_TYPE_RAM: u32 = 1; /// Errors thrown while configuring x86_64 system. -#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] +#[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum ConfigurationError { /// Invalid e820 setup params. E820Configuration, @@ -47,14 +76,31 @@ pub enum ConfigurationError { MpTableSetup(#[from] mptable::MptableError), /// Error writing the zero page of guest memory. ZeroPageSetup, - /// Failed to compute initrd address. - InitrdAddress, + /// Error writing module entry to guest memory. + ModlistSetup, + /// Error writing memory map table to guest memory. + MemmapTableSetup, + /// Error writing hvm_start_info to guest memory. + StartInfoSetup, + /// Cannot copy kernel file fd + KernelFile, + /// Cannot load kernel due to invalid memory configuration or invalid kernel image: {0} + KernelLoader(linux_loader::loader::Error), + /// Cannot load command line string: {0} + LoadCommandline(linux_loader::loader::Error), + /// Failed to create guest config: {0} + CreateGuestConfig(#[from] GuestConfigError), + /// Error configuring the vcpu for boot: {0} + VcpuConfigure(#[from] KvmVcpuConfigureError), + /// Error configuring ACPI: {0} + Acpi(#[from] crate::acpi::AcpiError), } -const FIRST_ADDR_PAST_32BITS: u64 = 1 << 32; +/// First address that cannot be addressed using 32 bit anymore. +pub const FIRST_ADDR_PAST_32BITS: u64 = 1 << 32; /// Size of MMIO gap at top of 32-bit address space. -pub const MEM_32BIT_GAP_SIZE: u64 = 768 << 20; +pub const MEM_32BIT_GAP_SIZE: u64 = mib_to_bytes(768) as u64; /// The start of the memory area reserved for MMIO devices. pub const MMIO_MEM_START: u64 = FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE; /// The size of the memory area reserved for MMIO devices. @@ -64,17 +110,33 @@ pub const MMIO_MEM_SIZE: u64 = MEM_32BIT_GAP_SIZE; /// These should be used to configure the GuestMemoryMmap structure for the platform. /// For x86_64 all addresses are valid from the start of the kernel except a /// carve out at the end of 32bit address space. -pub fn arch_memory_regions(size: usize) -> Vec<(GuestAddress, usize)> { +pub fn arch_memory_regions(offset: usize, size: usize) -> Vec<(GuestAddress, usize)> { + // If we get here with size == 0 something has seriously gone wrong. Firecracker should never + // try to allocate guest memory of size 0 + assert!(size > 0, "Attempt to allocate guest memory of length 0"); + assert!( + offset.checked_add(size).is_some(), + "Attempt to allocate guest memory such that the address space would wrap around" + ); + // It's safe to cast MMIO_MEM_START to usize because it fits in a u32 variable // (It points to an address in the 32 bit space). - match size.checked_sub(usize::try_from(MMIO_MEM_START).unwrap()) { + match (size + offset).checked_sub(u64_to_usize(MMIO_MEM_START)) { // case1: guest memory fits before the gap - None | Some(0) => vec![(GuestAddress(0), size)], - // case2: guest memory extends beyond the gap - Some(remaining) => vec![ - (GuestAddress(0), usize::try_from(MMIO_MEM_START).unwrap()), + None | Some(0) => vec![(GuestAddress(offset as u64), size)], + // case2: starts before the gap, but doesn't completely fit + Some(remaining) if (offset as u64) < MMIO_MEM_START => vec![ + ( + GuestAddress(offset as u64), + u64_to_usize(MMIO_MEM_START) - offset, + ), (GuestAddress(FIRST_ADDR_PAST_32BITS), remaining), ], + // case3: guest memory start after the gap + Some(_) => vec![( + GuestAddress(FIRST_ADDR_PAST_32BITS.max(offset as u64)), + size, + )], } } @@ -84,39 +146,200 @@ pub fn get_kernel_start() -> u64 { } /// Returns the memory address where the initrd could be loaded. -pub fn initrd_load_addr( - guest_mem: &GuestMemoryMmap, - initrd_size: usize, -) -> Result { - let first_region = guest_mem - .find_region(GuestAddress::new(0)) - .ok_or(ConfigurationError::InitrdAddress)?; +pub fn initrd_load_addr(guest_mem: &GuestMemoryMmap, initrd_size: usize) -> Option { + let first_region = guest_mem.find_region(GuestAddress::new(0))?; let lowmem_size = u64_to_usize(first_region.len()); if lowmem_size < initrd_size { - return Err(ConfigurationError::InitrdAddress); + return None; + } + + Some(align_down( + usize_to_u64(lowmem_size - initrd_size), + usize_to_u64(super::GUEST_PAGE_SIZE), + )) +} + +/// Configures the system for booting Linux. +pub fn configure_system_for_boot( + vmm: &mut Vmm, + vcpus: &mut [Vcpu], + machine_config: &MachineConfig, + cpu_template: &CustomCpuTemplate, + entry_point: EntryPoint, + initrd: &Option, + boot_cmdline: Cmdline, +) -> Result<(), ConfigurationError> { + // Construct the base CpuConfiguration to apply CPU template onto. + let cpu_config = + CpuConfiguration::new(vmm.kvm.supported_cpuid.clone(), cpu_template, &vcpus[0])?; + // Apply CPU template to the base CpuConfiguration. + let cpu_config = CpuConfiguration::apply_template(cpu_config, cpu_template)?; + + let vcpu_config = VcpuConfig { + vcpu_count: machine_config.vcpu_count, + smt: machine_config.smt, + cpu_config, + }; + + // Configure vCPUs with normalizing and setting the generated CPU configuration. + for vcpu in vcpus.iter_mut() { + vcpu.kvm_vcpu + .configure(vmm.vm.guest_memory(), entry_point, &vcpu_config)?; + } + + // Write the kernel command line to guest memory. This is x86_64 specific, since on + // aarch64 the command line will be specified through the FDT. + let cmdline_size = boot_cmdline + .as_cstring() + .map(|cmdline_cstring| cmdline_cstring.as_bytes_with_nul().len()) + .expect("Cannot create cstring from cmdline string"); + + load_cmdline( + vmm.vm.guest_memory(), + GuestAddress(crate::arch::x86_64::layout::CMDLINE_START), + &boot_cmdline, + ) + .map_err(ConfigurationError::LoadCommandline)?; + + // Note that this puts the mptable at the last 1k of Linux's 640k base RAM + mptable::setup_mptable( + vmm.vm.guest_memory(), + &mut vmm.resource_allocator, + vcpu_config.vcpu_count, + ) + .map_err(ConfigurationError::MpTableSetup)?; + + match entry_point.protocol { + BootProtocol::PvhBoot => { + configure_pvh(vmm.vm.guest_memory(), GuestAddress(CMDLINE_START), initrd)?; + } + BootProtocol::LinuxBoot => { + configure_64bit_boot( + vmm.vm.guest_memory(), + GuestAddress(CMDLINE_START), + cmdline_size, + initrd, + )?; + } } - let align_to_pagesize = |address| address & !(super::PAGE_SIZE - 1); - Ok(align_to_pagesize(lowmem_size - initrd_size) as u64) + // Create ACPI tables and write them in guest memory + // For the time being we only support ACPI in x86_64 + create_acpi_tables( + vmm.vm.guest_memory(), + &mut vmm.resource_allocator, + &vmm.mmio_device_manager, + &vmm.acpi_device_manager, + vcpus, + )?; + Ok(()) } -/// Configures the system and should be called once per vm before starting vcpu threads. -/// -/// # Arguments -/// -/// * `guest_mem` - The memory to be used by the guest. -/// * `cmdline_addr` - Address in `guest_mem` where the kernel command line was loaded. -/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator. -/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`. -/// * `num_cpus` - Number of virtual CPUs the guest will have. -pub fn configure_system( +fn configure_pvh( + guest_mem: &GuestMemoryMmap, + cmdline_addr: GuestAddress, + initrd: &Option, +) -> Result<(), ConfigurationError> { + const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336e_c578; + let first_addr_past_32bits = GuestAddress(FIRST_ADDR_PAST_32BITS); + let end_32bit_gap_start = GuestAddress(MMIO_MEM_START); + let himem_start = GuestAddress(layout::HIMEM_START); + + // Vector to hold modules (currently either empty or holding initrd). + let mut modules: Vec = Vec::new(); + if let Some(initrd_config) = initrd { + // The initrd has been written to guest memory already, here we just need to + // create the module structure that describes it. + modules.push(hvm_modlist_entry { + paddr: initrd_config.address.raw_value(), + size: initrd_config.size as u64, + ..Default::default() + }); + } + + // Vector to hold the memory maps which needs to be written to guest memory + // at MEMMAP_START after all of the mappings are recorded. + let mut memmap: Vec = Vec::new(); + + // Create the memory map entries. + memmap.push(hvm_memmap_table_entry { + addr: 0, + size: SYSTEM_MEM_START, + type_: MEMMAP_TYPE_RAM, + ..Default::default() + }); + memmap.push(hvm_memmap_table_entry { + addr: SYSTEM_MEM_START, + size: SYSTEM_MEM_SIZE, + type_: E820_RESERVED, + ..Default::default() + }); + let last_addr = guest_mem.last_addr(); + if last_addr < end_32bit_gap_start { + memmap.push(hvm_memmap_table_entry { + addr: himem_start.raw_value(), + size: last_addr.unchecked_offset_from(himem_start) + 1, + type_: MEMMAP_TYPE_RAM, + ..Default::default() + }); + } else { + memmap.push(hvm_memmap_table_entry { + addr: himem_start.raw_value(), + size: end_32bit_gap_start.unchecked_offset_from(himem_start), + type_: MEMMAP_TYPE_RAM, + ..Default::default() + }); + + if last_addr > first_addr_past_32bits { + memmap.push(hvm_memmap_table_entry { + addr: first_addr_past_32bits.raw_value(), + size: last_addr.unchecked_offset_from(first_addr_past_32bits) + 1, + type_: MEMMAP_TYPE_RAM, + ..Default::default() + }); + } + } + + // Construct the hvm_start_info structure and serialize it into + // boot_params. This will be stored at PVH_INFO_START address, and %rbx + // will be initialized to contain PVH_INFO_START prior to starting the + // guest, as required by the PVH ABI. + #[allow(clippy::cast_possible_truncation)] // the vec lengths are single digit integers + let mut start_info = hvm_start_info { + magic: XEN_HVM_START_MAGIC_VALUE, + version: 1, + cmdline_paddr: cmdline_addr.raw_value(), + memmap_paddr: layout::MEMMAP_START, + memmap_entries: memmap.len() as u32, + nr_modules: modules.len() as u32, + ..Default::default() + }; + if !modules.is_empty() { + start_info.modlist_paddr = layout::MODLIST_START; + } + let mut boot_params = + BootParams::new::(&start_info, GuestAddress(layout::PVH_INFO_START)); + + // Copy the vector with the memmap table to the MEMMAP_START address + // which is already saved in the memmap_paddr field of hvm_start_info struct. + boot_params.set_sections::(&memmap, GuestAddress(layout::MEMMAP_START)); + + // Copy the vector with the modules list to the MODLIST_START address. + // Note that we only set the modlist_paddr address if there is a nonzero + // number of modules, but serializing an empty list is harmless. + boot_params.set_modules::(&modules, GuestAddress(layout::MODLIST_START)); + + // Write the hvm_start_info struct to guest memory. + PvhBootConfigurator::write_bootparams(&boot_params, guest_mem) + .map_err(|_| ConfigurationError::StartInfoSetup) +} + +fn configure_64bit_boot( guest_mem: &GuestMemoryMmap, - resource_allocator: &mut ResourceAllocator, cmdline_addr: GuestAddress, cmdline_size: usize, initrd: &Option, - num_cpus: u8, ) -> Result<(), ConfigurationError> { const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55; const KERNEL_HDR_MAGIC: u32 = 0x5372_6448; @@ -127,9 +350,6 @@ pub fn configure_system( let himem_start = GuestAddress(layout::HIMEM_START); - // Note that this puts the mptable at the last 1k of Linux's 640k base RAM - mptable::setup_mptable(guest_mem, resource_allocator, num_cpus)?; - // Set the location of RSDP in Boot Parameters to help the guest kernel find it faster. let mut params = boot_params { acpi_rsdp_addr: layout::RSDP_ADDR, @@ -217,27 +437,136 @@ fn add_e820_entry( Ok(()) } +/// Load linux kernel into guest memory. +pub fn load_kernel( + kernel: &File, + guest_memory: &GuestMemoryMmap, +) -> Result { + // Need to clone the File because reading from it + // mutates it. + let mut kernel_file = kernel + .try_clone() + .map_err(|_| ConfigurationError::KernelFile)?; + + let entry_addr = Loader::load( + guest_memory, + None, + &mut kernel_file, + Some(GuestAddress(get_kernel_start())), + ) + .map_err(ConfigurationError::KernelLoader)?; + + let mut entry_point_addr: GuestAddress = entry_addr.kernel_load; + let mut boot_prot: BootProtocol = BootProtocol::LinuxBoot; + if let PvhBootCapability::PvhEntryPresent(pvh_entry_addr) = entry_addr.pvh_boot_cap { + // Use the PVH kernel entry point to boot the guest + entry_point_addr = pvh_entry_addr; + boot_prot = BootProtocol::PvhBoot; + } + + debug!("Kernel loaded using {boot_prot}"); + + Ok(EntryPoint { + entry_addr: entry_point_addr, + protocol: boot_prot, + }) +} + +#[cfg(kani)] +mod verification { + use crate::arch::x86_64::FIRST_ADDR_PAST_32BITS; + use crate::arch::{MMIO_MEM_START, arch_memory_regions}; + + #[kani::proof] + #[kani::unwind(3)] + fn verify_arch_memory_regions() { + let offset: u64 = kani::any::(); + let len: u64 = kani::any::(); + + kani::assume(len > 0); + kani::assume(offset.checked_add(len).is_some()); + + let regions = arch_memory_regions(offset as usize, len as usize); + + // There's only one MMIO gap, so we can get either 1 or 2 regions + assert!(regions.len() <= 2); + assert!(regions.len() >= 1); + + // The total length of all regions is what we requested + assert_eq!( + regions.iter().map(|&(_, len)| len).sum::(), + len as usize + ); + + // No region overlaps the MMIO gap + assert!( + regions + .iter() + .all(|&(start, len)| start.0 >= FIRST_ADDR_PAST_32BITS + || start.0 + len as u64 <= MMIO_MEM_START) + ); + + // All regions start after our specified offset + assert!(regions.iter().all(|&(start, _)| start.0 >= offset as u64)); + + // All regions have non-zero length + assert!(regions.iter().all(|&(_, len)| len > 0)); + + // If there's two regions, they perfectly snuggle up to the MMIO gap + if regions.len() == 2 { + kani::cover!(); + + assert_eq!(regions[0].0.0 + regions[0].1 as u64, MMIO_MEM_START); + assert_eq!(regions[1].0.0, FIRST_ADDR_PAST_32BITS); + } + } +} + #[cfg(test)] mod tests { use linux_loader::loader::bootparam::boot_e820_entry; use super::*; + use crate::device_manager::resources::ResourceAllocator; use crate::test_utils::{arch_mem, single_region_mem}; #[test] fn regions_lt_4gb() { - let regions = arch_memory_regions(1usize << 29); + let regions = arch_memory_regions(0, 1usize << 29); assert_eq!(1, regions.len()); assert_eq!(GuestAddress(0), regions[0].0); assert_eq!(1usize << 29, regions[0].1); + + let regions = arch_memory_regions(1 << 28, 1 << 29); + assert_eq!(1, regions.len()); + assert_eq!(regions[0], (GuestAddress(1 << 28), 1 << 29)); } #[test] fn regions_gt_4gb() { - let regions = arch_memory_regions((1usize << 32) + 0x8000); + const MEMORY_SIZE: usize = (1 << 32) + 0x8000; + + let regions = arch_memory_regions(0, MEMORY_SIZE); assert_eq!(2, regions.len()); assert_eq!(GuestAddress(0), regions[0].0); assert_eq!(GuestAddress(1u64 << 32), regions[1].0); + + let regions = arch_memory_regions(1 << 31, MEMORY_SIZE); + assert_eq!(2, regions.len()); + assert_eq!( + regions[0], + ( + GuestAddress(1 << 31), + u64_to_usize(MMIO_MEM_START) - (1 << 31) + ) + ); + assert_eq!( + regions[1], + ( + GuestAddress(FIRST_ADDR_PAST_32BITS), + MEMORY_SIZE - regions[0].1 + ) + ) } #[test] @@ -245,54 +574,35 @@ mod tests { let no_vcpus = 4; let gm = single_region_mem(0x10000); let mut resource_allocator = ResourceAllocator::new().unwrap(); - let config_err = - configure_system(&gm, &mut resource_allocator, GuestAddress(0), 0, &None, 1); - assert_eq!( - config_err.unwrap_err(), - super::ConfigurationError::MpTableSetup(mptable::MptableError::NotEnoughMemory) - ); + let err = mptable::setup_mptable(&gm, &mut resource_allocator, 1); + assert!(matches!( + err.unwrap_err(), + mptable::MptableError::NotEnoughMemory + )); // Now assigning some memory that falls before the 32bit memory hole. - let mem_size = 128 << 20; + let mem_size = mib_to_bytes(128); let gm = arch_mem(mem_size); let mut resource_allocator = ResourceAllocator::new().unwrap(); - configure_system( - &gm, - &mut resource_allocator, - GuestAddress(0), - 0, - &None, - no_vcpus, - ) - .unwrap(); + mptable::setup_mptable(&gm, &mut resource_allocator, no_vcpus).unwrap(); + configure_64bit_boot(&gm, GuestAddress(0), 0, &None).unwrap(); + configure_pvh(&gm, GuestAddress(0), &None).unwrap(); // Now assigning some memory that is equal to the start of the 32bit memory hole. - let mem_size = 3328 << 20; + let mem_size = mib_to_bytes(3328); let gm = arch_mem(mem_size); let mut resource_allocator = ResourceAllocator::new().unwrap(); - configure_system( - &gm, - &mut resource_allocator, - GuestAddress(0), - 0, - &None, - no_vcpus, - ) - .unwrap(); + mptable::setup_mptable(&gm, &mut resource_allocator, no_vcpus).unwrap(); + configure_64bit_boot(&gm, GuestAddress(0), 0, &None).unwrap(); + configure_pvh(&gm, GuestAddress(0), &None).unwrap(); // Now assigning some memory that falls after the 32bit memory hole. - let mem_size = 3330 << 20; + let mem_size = mib_to_bytes(3330); let gm = arch_mem(mem_size); let mut resource_allocator = ResourceAllocator::new().unwrap(); - configure_system( - &gm, - &mut resource_allocator, - GuestAddress(0), - 0, - &None, - no_vcpus, - ) - .unwrap(); + mptable::setup_mptable(&gm, &mut resource_allocator, no_vcpus).unwrap(); + configure_64bit_boot(&gm, GuestAddress(0), 0, &None).unwrap(); + configure_pvh(&gm, GuestAddress(0), &None).unwrap(); } #[test] @@ -326,12 +636,14 @@ mod tests { // Exercise the scenario where the field storing the length of the e820 entry table is // is bigger than the allocated memory. params.e820_entries = u8::try_from(params.e820_table.len()).unwrap() + 1; - assert!(add_e820_entry( - &mut params, - e820_map[0].addr, - e820_map[0].size, - e820_map[0].type_ - ) - .is_err()); + assert!( + add_e820_entry( + &mut params, + e820_map[0].addr, + e820_map[0].size, + e820_map[0].type_ + ) + .is_err() + ); } } diff --git a/src/vmm/src/arch/x86_64/mptable.rs b/src/vmm/src/arch/x86_64/mptable.rs index 9944ecee8fb..6646c17e282 100644 --- a/src/vmm/src/arch/x86_64/mptable.rs +++ b/src/vmm/src/arch/x86_64/mptable.rs @@ -13,8 +13,8 @@ use libc::c_char; use log::debug; use vm_allocator::AllocPolicy; -use crate::arch::x86_64::gen::mpspec; use crate::arch::IRQ_MAX; +use crate::arch::x86_64::generated::mpspec; use crate::device_manager::resources::ResourceAllocator; use crate::vstate::memory::{ Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, diff --git a/src/vmm/src/arch/x86_64/msr.rs b/src/vmm/src/arch/x86_64/msr.rs index 325d6ed6b29..f8674c6c3a5 100644 --- a/src/vmm/src/arch/x86_64/msr.rs +++ b/src/vmm/src/arch/x86_64/msr.rs @@ -3,13 +3,13 @@ /// Model Specific Registers (MSRs) related functionality. use bitflags::bitflags; -use kvm_bindings::{kvm_msr_entry, MsrList, Msrs}; +use kvm_bindings::{MsrList, Msrs, kvm_msr_entry}; use kvm_ioctls::{Kvm, VcpuFd}; -use crate::arch::x86_64::gen::hyperv::*; -use crate::arch::x86_64::gen::hyperv_tlfs::*; -use crate::arch::x86_64::gen::msr_index::*; -use crate::arch::x86_64::gen::perf_event::*; +use crate::arch::x86_64::generated::hyperv::*; +use crate::arch::x86_64::generated::hyperv_tlfs::*; +use crate::arch::x86_64::generated::msr_index::*; +use crate::arch::x86_64::generated::perf_event::*; use crate::cpu_config::x86_64::cpuid::common::GetCpuidError; #[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] diff --git a/src/vmm/src/arch/x86_64/regs.rs b/src/vmm/src/arch/x86_64/regs.rs index aec47677c4c..0a4cf630bf3 100644 --- a/src/vmm/src/arch/x86_64/regs.rs +++ b/src/vmm/src/arch/x86_64/regs.rs @@ -1,3 +1,4 @@ +// Copyright © 2020, Oracle and/or its affiliates. // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // @@ -10,6 +11,7 @@ use std::mem; use kvm_bindings::{kvm_fpu, kvm_regs, kvm_sregs}; use kvm_ioctls::VcpuFd; +use super::super::{BootProtocol, EntryPoint}; use super::gdt::{gdt_entry, kvm_segment_from_gdt}; use crate::vstate::memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap}; @@ -80,20 +82,30 @@ pub struct SetupRegistersError(vmm_sys_util::errno::Error); /// # Errors /// /// When [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_regs`] errors. -pub fn setup_regs(vcpu: &VcpuFd, boot_ip: u64) -> Result<(), SetupRegistersError> { - let regs: kvm_regs = kvm_regs { - rflags: 0x0000_0000_0000_0002u64, - rip: boot_ip, - // Frame pointer. It gets a snapshot of the stack pointer (rsp) so that when adjustments are - // made to rsp (i.e. reserving space for local variables or pushing values on to the stack), - // local variables and function parameters are still accessible from a constant offset from - // rbp. - rsp: super::layout::BOOT_STACK_POINTER, - // Starting stack pointer. - rbp: super::layout::BOOT_STACK_POINTER, - // Must point to zero page address per Linux ABI. This is x86_64 specific. - rsi: super::layout::ZERO_PAGE_START, - ..Default::default() +pub fn setup_regs(vcpu: &VcpuFd, entry_point: EntryPoint) -> Result<(), SetupRegistersError> { + let regs: kvm_regs = match entry_point.protocol { + BootProtocol::PvhBoot => kvm_regs { + // Configure regs as required by PVH boot protocol. + rflags: 0x0000_0000_0000_0002u64, + rbx: super::layout::PVH_INFO_START, + rip: entry_point.entry_addr.raw_value(), + ..Default::default() + }, + BootProtocol::LinuxBoot => kvm_regs { + // Configure regs as required by Linux 64-bit boot protocol. + rflags: 0x0000_0000_0000_0002u64, + rip: entry_point.entry_addr.raw_value(), + // Frame pointer. It gets a snapshot of the stack pointer (rsp) so that when adjustments + // are made to rsp (i.e. reserving space for local variables or pushing + // values on to the stack), local variables and function parameters are + // still accessible from a constant offset from rbp. + rsp: super::layout::BOOT_STACK_POINTER, + // Starting stack pointer. + rbp: super::layout::BOOT_STACK_POINTER, + // Must point to zero page address per Linux ABI. This is x86_64 specific. + rsi: super::layout::ZERO_PAGE_START, + ..Default::default() + }, }; vcpu.set_regs(®s).map_err(SetupRegistersError) @@ -118,6 +130,7 @@ pub enum SetupSpecialRegistersError { /// /// * `mem` - The memory that will be passed to the guest. /// * `vcpu` - Structure for the VCPU that holds the VCPU's fd. +/// * `boot_prot` - The boot protocol being used. /// /// # Errors /// @@ -126,14 +139,21 @@ pub enum SetupSpecialRegistersError { /// - [`configure_segments_and_sregs`] errors. /// - [`setup_page_tables`] errors /// - [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_sregs`] errors. -pub fn setup_sregs(mem: &GuestMemoryMmap, vcpu: &VcpuFd) -> Result<(), SetupSpecialRegistersError> { +pub fn setup_sregs( + mem: &GuestMemoryMmap, + vcpu: &VcpuFd, + boot_prot: BootProtocol, +) -> Result<(), SetupSpecialRegistersError> { let mut sregs: kvm_sregs = vcpu .get_sregs() .map_err(SetupSpecialRegistersError::GetSpecialRegisters)?; - configure_segments_and_sregs(mem, &mut sregs) + configure_segments_and_sregs(mem, &mut sregs, boot_prot) .map_err(SetupSpecialRegistersError::ConfigureSegmentsAndSpecialRegisters)?; - setup_page_tables(mem, &mut sregs).map_err(SetupSpecialRegistersError::SetupPageTables)?; // TODO(dgreid) - Can this be done once per system instead? + if let BootProtocol::LinuxBoot = boot_prot { + setup_page_tables(mem, &mut sregs).map_err(SetupSpecialRegistersError::SetupPageTables)?; + // TODO(dgreid) - Can this be done once per system instead? + } vcpu.set_sregs(&sregs) .map_err(SetupSpecialRegistersError::SetSpecialRegisters) @@ -148,6 +168,7 @@ const EFER_LMA: u64 = 0x400; const EFER_LME: u64 = 0x100; const X86_CR0_PE: u64 = 0x1; +const X86_CR0_ET: u64 = 0x10; const X86_CR0_PG: u64 = 0x8000_0000; const X86_CR4_PAE: u64 = 0x20; @@ -174,13 +195,28 @@ fn write_idt_value(val: u64, guest_mem: &GuestMemoryMmap) -> Result<(), RegsErro fn configure_segments_and_sregs( mem: &GuestMemoryMmap, sregs: &mut kvm_sregs, + boot_prot: BootProtocol, ) -> Result<(), RegsError> { - let gdt_table: [u64; BOOT_GDT_MAX] = [ - gdt_entry(0, 0, 0), // NULL - gdt_entry(0xa09b, 0, 0xfffff), // CODE - gdt_entry(0xc093, 0, 0xfffff), // DATA - gdt_entry(0x808b, 0, 0xfffff), // TSS - ]; + let gdt_table: [u64; BOOT_GDT_MAX] = match boot_prot { + BootProtocol::PvhBoot => { + // Configure GDT entries as specified by PVH boot protocol + [ + gdt_entry(0, 0, 0), // NULL + gdt_entry(0xc09b, 0, 0xffff_ffff), // CODE + gdt_entry(0xc093, 0, 0xffff_ffff), // DATA + gdt_entry(0x008b, 0, 0x67), // TSS + ] + } + BootProtocol::LinuxBoot => { + // Configure GDT entries as specified by Linux 64bit boot protocol + [ + gdt_entry(0, 0, 0), // NULL + gdt_entry(0xa09b, 0, 0xfffff), // CODE + gdt_entry(0xc093, 0, 0xfffff), // DATA + gdt_entry(0x808b, 0, 0xfffff), // TSS + ] + } + }; let code_seg = kvm_segment_from_gdt(gdt_table[1], 1); let data_seg = kvm_segment_from_gdt(gdt_table[2], 2); @@ -203,9 +239,17 @@ fn configure_segments_and_sregs( sregs.ss = data_seg; sregs.tr = tss_seg; - // 64-bit protected mode - sregs.cr0 |= X86_CR0_PE; - sregs.efer |= EFER_LME | EFER_LMA; + match boot_prot { + BootProtocol::PvhBoot => { + sregs.cr0 = X86_CR0_PE | X86_CR0_ET; + sregs.cr4 = 0; + } + BootProtocol::LinuxBoot => { + // 64-bit protected mode + sregs.cr0 |= X86_CR0_PE; + sregs.efer |= EFER_LME | EFER_LMA; + } + } Ok(()) } @@ -251,24 +295,45 @@ mod tests { gm.read_obj(read_addr).unwrap() } - fn validate_segments_and_sregs(gm: &GuestMemoryMmap, sregs: &kvm_sregs) { + fn validate_segments_and_sregs( + gm: &GuestMemoryMmap, + sregs: &kvm_sregs, + boot_prot: BootProtocol, + ) { + if let BootProtocol::LinuxBoot = boot_prot { + assert_eq!(0xaf_9b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 8)); + assert_eq!(0xcf_9300_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 16)); + assert_eq!(0x8f_8b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 24)); + + assert_eq!(0xffff_ffff, sregs.tr.limit); + + assert!(sregs.cr0 & X86_CR0_PE != 0); + assert!(sregs.efer & EFER_LME != 0 && sregs.efer & EFER_LMA != 0); + } else { + // Validate values that are specific to PVH boot protocol + assert_eq!(0xcf_9b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 8)); + assert_eq!(0xcf_9300_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 16)); + assert_eq!(0x00_8b00_0000_0067, read_u64(gm, BOOT_GDT_OFFSET + 24)); + + assert_eq!(0x67, sregs.tr.limit); + assert_eq!(0, sregs.tr.g); + + assert!(sregs.cr0 & X86_CR0_PE != 0 && sregs.cr0 & X86_CR0_ET != 0); + assert_eq!(0, sregs.cr4); + } + + // Common settings for both PVH and Linux boot protocol assert_eq!(0x0, read_u64(gm, BOOT_GDT_OFFSET)); - assert_eq!(0xaf_9b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 8)); - assert_eq!(0xcf_9300_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 16)); - assert_eq!(0x8f_8b00_0000_ffff, read_u64(gm, BOOT_GDT_OFFSET + 24)); assert_eq!(0x0, read_u64(gm, BOOT_IDT_OFFSET)); assert_eq!(0, sregs.cs.base); - assert_eq!(0xfffff, sregs.ds.limit); + assert_eq!(0xffff_ffff, sregs.ds.limit); assert_eq!(0x10, sregs.es.selector); assert_eq!(1, sregs.fs.present); assert_eq!(1, sregs.gs.g); assert_eq!(0, sregs.ss.avl); assert_eq!(0, sregs.tr.base); - assert_eq!(0xfffff, sregs.tr.limit); assert_eq!(0, sregs.tr.avl); - assert!(sregs.cr0 & X86_CR0_PE != 0); - assert!(sregs.efer & EFER_LME != 0 && sregs.efer & EFER_LMA != 0); } fn validate_page_tables(gm: &GuestMemoryMmap, sregs: &kvm_sregs) { @@ -320,7 +385,12 @@ mod tests { ..Default::default() }; - setup_regs(&vcpu, expected_regs.rip).unwrap(); + let entry_point: EntryPoint = EntryPoint { + entry_addr: GuestAddress(expected_regs.rip), + protocol: BootProtocol::LinuxBoot, + }; + + setup_regs(&vcpu, entry_point).unwrap(); let actual_regs: kvm_regs = vcpu.get_regs().unwrap(); assert_eq!(actual_regs, expected_regs); @@ -333,16 +403,22 @@ mod tests { let vcpu = vm.create_vcpu(0).unwrap(); let gm = single_region_mem(0x10000); - vcpu.set_sregs(&Default::default()).unwrap(); - setup_sregs(&gm, &vcpu).unwrap(); - - let mut sregs: kvm_sregs = vcpu.get_sregs().unwrap(); - // for AMD KVM_GET_SREGS returns g = 0 for each kvm_segment. - // We set it to 1, otherwise the test will fail. - sregs.gs.g = 1; - - validate_segments_and_sregs(&gm, &sregs); - validate_page_tables(&gm, &sregs); + [BootProtocol::LinuxBoot, BootProtocol::PvhBoot] + .iter() + .for_each(|boot_prot| { + vcpu.set_sregs(&Default::default()).unwrap(); + setup_sregs(&gm, &vcpu, *boot_prot).unwrap(); + + let mut sregs: kvm_sregs = vcpu.get_sregs().unwrap(); + // for AMD KVM_GET_SREGS returns g = 0 for each kvm_segment. + // We set it to 1, otherwise the test will fail. + sregs.gs.g = 1; + + validate_segments_and_sregs(&gm, &sregs, *boot_prot); + if let BootProtocol::LinuxBoot = *boot_prot { + validate_page_tables(&gm, &sregs); + } + }); } #[test] @@ -386,9 +462,13 @@ mod tests { fn test_configure_segments_and_sregs() { let mut sregs: kvm_sregs = Default::default(); let gm = single_region_mem(0x10000); - configure_segments_and_sregs(&gm, &mut sregs).unwrap(); + configure_segments_and_sregs(&gm, &mut sregs, BootProtocol::LinuxBoot).unwrap(); + + validate_segments_and_sregs(&gm, &sregs, BootProtocol::LinuxBoot); + + configure_segments_and_sregs(&gm, &mut sregs, BootProtocol::PvhBoot).unwrap(); - validate_segments_and_sregs(&gm, &sregs); + validate_segments_and_sregs(&gm, &sregs, BootProtocol::PvhBoot); } #[test] diff --git a/src/vmm/src/vstate/vcpu/x86_64.rs b/src/vmm/src/arch/x86_64/vcpu.rs similarity index 82% rename from src/vmm/src/vstate/vcpu/x86_64.rs rename to src/vmm/src/arch/x86_64/vcpu.rs index 6fee3933435..6c07720b573 100644 --- a/src/vmm/src/vstate/vcpu/x86_64.rs +++ b/src/vmm/src/arch/x86_64/vcpu.rs @@ -9,22 +9,23 @@ use std::collections::BTreeMap; use std::fmt::Debug; use kvm_bindings::{ - kvm_debugregs, kvm_lapic_state, kvm_mp_state, kvm_regs, kvm_sregs, kvm_vcpu_events, kvm_xcrs, - kvm_xsave, CpuId, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MAX_MSR_ENTRIES, + CpuId, KVM_MAX_CPUID_ENTRIES, KVM_MAX_MSR_ENTRIES, Msrs, Xsave, kvm_debugregs, kvm_lapic_state, + kvm_mp_state, kvm_regs, kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, kvm_xsave2, }; use kvm_ioctls::{VcpuExit, VcpuFd}; use log::{error, warn}; use serde::{Deserialize, Serialize}; -use vmm_sys_util::fam; +use vmm_sys_util::fam::{self, FamStruct}; -use crate::arch::x86_64::gen::msr_index::{MSR_IA32_TSC, MSR_IA32_TSC_DEADLINE}; +use crate::arch::EntryPoint; +use crate::arch::x86_64::generated::msr_index::{MSR_IA32_TSC, MSR_IA32_TSC_DEADLINE}; use crate::arch::x86_64::interrupts; -use crate::arch::x86_64::msr::{create_boot_msr_entries, MsrError}; +use crate::arch::x86_64::msr::{MsrError, create_boot_msr_entries}; use crate::arch::x86_64::regs::{SetupFpuError, SetupRegistersError, SetupSpecialRegistersError}; -use crate::cpu_config::x86_64::{cpuid, CpuConfiguration}; +use crate::cpu_config::x86_64::{CpuConfiguration, cpuid}; use crate::logger::{IncMetric, METRICS}; -use crate::vstate::memory::{Address, GuestAddress, GuestMemoryMmap}; -use crate::vstate::vcpu::{VcpuConfig, VcpuEmulation}; +use crate::vstate::memory::GuestMemoryMmap; +use crate::vstate::vcpu::{VcpuConfig, VcpuEmulation, VcpuError}; use crate::vstate::vm::Vm; // Tolerance for TSC frequency expected variation. @@ -61,7 +62,7 @@ pub enum KvmVcpuError { VcpuGetLapic(kvm_ioctls::Error), /// Failed to get KVM vcpu mp state: {0} VcpuGetMpState(kvm_ioctls::Error), - /// Failed to get KVM vcpu msr: 0x{0:x} + /// Failed to get KVM vcpu msr: {0:#x} VcpuGetMsr(u32), /// Failed to get KVM vcpu msrs: {0} VcpuGetMsrs(kvm_ioctls::Error), @@ -73,8 +74,10 @@ pub enum KvmVcpuError { VcpuGetVcpuEvents(kvm_ioctls::Error), /// Failed to get KVM vcpu xcrs: {0} VcpuGetXcrs(kvm_ioctls::Error), - /// Failed to get KVM vcpu xsave: {0} + /// Failed to get KVM vcpu xsave via KVM_GET_XSAVE: {0} VcpuGetXsave(kvm_ioctls::Error), + /// Failed to get KVM vcpu xsave via KVM_GET_XSAVE2: {0} + VcpuGetXsave2(kvm_ioctls::Error), /// Failed to get KVM vcpu cpuid: {0} VcpuGetCpuid(kvm_ioctls::Error), /// Failed to get KVM TSC frequency: {0} @@ -142,15 +145,19 @@ pub struct KvmVcpu { /// KVM vcpu fd. pub fd: VcpuFd, /// Vcpu peripherals, such as buses - pub(super) peripherals: Peripherals, + pub peripherals: Peripherals, /// The list of MSRs to include in a VM snapshot, in the same order as KVM returned them /// from KVM_GET_MSR_INDEX_LIST msrs_to_save: Vec, + /// Size in bytes requiring to hold the dynamically-sized `kvm_xsave` struct. + /// + /// `None` if `KVM_CAP_XSAVE2` not supported. + xsave2_size: Option, } /// Vcpu peripherals #[derive(Default, Debug)] -pub(super) struct Peripherals { +pub struct Peripherals { /// Pio bus. pub pio_bus: Option, /// Mmio bus. @@ -174,7 +181,8 @@ impl KvmVcpu { index, fd: kvm_vcpu, peripherals: Default::default(), - msrs_to_save: vm.msrs_to_save().as_slice().to_vec(), + msrs_to_save: vm.msrs_to_save().to_vec(), + xsave2_size: vm.xsave2_size(), }) } @@ -183,13 +191,14 @@ impl KvmVcpu { /// # Arguments /// /// * `guest_mem` - The guest memory used by this microvm. - /// * `kernel_start_addr` - Offset from `guest_mem` at which the kernel starts. + /// * `kernel_entry_point` - Specifies the boot protocol and offset from `guest_mem` at which + /// the kernel starts. /// * `vcpu_config` - The vCPU configuration. /// * `cpuid` - The capabilities exposed by this vCPU. pub fn configure( &mut self, guest_mem: &GuestMemoryMmap, - kernel_start_addr: GuestAddress, + kernel_entry_point: EntryPoint, vcpu_config: &VcpuConfig, ) -> Result<(), KvmVcpuConfigureError> { let mut cpuid = vcpu_config.cpu_config.cpuid.clone(); @@ -249,11 +258,10 @@ impl KvmVcpu { .collect::>(); crate::arch::x86_64::msr::set_msrs(&self.fd, &kvm_msrs)?; - crate::arch::x86_64::regs::setup_regs(&self.fd, kernel_start_addr.raw_value())?; + crate::arch::x86_64::regs::setup_regs(&self.fd, kernel_entry_point)?; crate::arch::x86_64::regs::setup_fpu(&self.fd)?; - crate::arch::x86_64::regs::setup_sregs(guest_mem, &self.fd)?; + crate::arch::x86_64::regs::setup_sregs(guest_mem, &self.fd, kernel_entry_point.protocol)?; crate::arch::x86_64::interrupts::set_lint(&self.fd)?; - Ok(()) } @@ -262,11 +270,71 @@ impl KvmVcpu { self.peripherals.pio_bus = Some(pio_bus); } + /// Get the current XSAVE state for this vCPU. + /// + /// The C `kvm_xsave` struct was extended by adding a flexible array member (FAM) in the end + /// to support variable-sized XSTATE buffer. + /// + /// https://elixir.bootlin.com/linux/v6.13.6/source/arch/x86/include/uapi/asm/kvm.h#L381 + /// ```c + /// struct kvm_xsave { + /// __u32 region[1024]; + /// __u32 extra[]; + /// }; + /// ``` + /// + /// As shown above, the C `kvm_xsave` struct does not have any field for the size of itself or + /// the length of its FAM. The required size (in bytes) of `kvm_xsave` struct can be retrieved + /// via `KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2)`. + /// + /// kvm-bindings defines `kvm_xsave2` struct that wraps the `kvm_xsave` struct to have `len` + /// field that indicates the number of FAM entries (i.e. `extra`), it also defines `Xsave` as + /// a `FamStructWrapper` of `kvm_xsave2`. + /// + /// https://github.com/rust-vmm/kvm/blob/68fff5491703bf32bd35656f7ba994a4cae9ea7d/kvm-bindings/src/x86_64/fam_wrappers.rs#L106 + /// ```rs + /// pub struct kvm_xsave2 { + /// pub len: usize, + /// pub xsave: kvm_xsave, + /// } + /// ``` + fn get_xsave(&self) -> Result { + match self.xsave2_size { + // if `KVM_CAP_XSAVE2` supported + Some(xsave2_size) => { + // Convert the `kvm_xsave` size in bytes to the length of FAM (i.e. `extra`). + let fam_len = + // Calculate the size of FAM (`extra`) area in bytes. Note that the subtraction + // never underflows because `KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2)` always returns + // at least 4096 bytes that is the size of `kvm_xsave` without FAM area. + (xsave2_size - std::mem::size_of::()) + // Divide by the size of FAM (`extra`) entry (i.e. `__u32`). + .div_ceil(std::mem::size_of::<::Entry>()); + let mut xsave = Xsave::new(fam_len).map_err(KvmVcpuError::Fam)?; + // SAFETY: Safe because `xsave` is allocated with enough size to save XSTATE. + unsafe { self.fd.get_xsave2(&mut xsave) }.map_err(KvmVcpuError::VcpuGetXsave2)?; + Ok(xsave) + } + // if `KVM_CAP_XSAVE2` not supported + None => Ok( + // SAFETY: The content is correctly laid out. + unsafe { + Xsave::from_raw(vec![kvm_xsave2 { + // Note that `len` is the number of FAM (`extra`) entries that didn't exist + // on older kernels not supporting `KVM_CAP_XSAVE2`. Thus, it's always zero. + len: 0, + xsave: self.fd.get_xsave().map_err(KvmVcpuError::VcpuGetXsave)?, + }]) + }, + ), + } + } + /// Get the current TSC frequency for this vCPU. /// /// # Errors /// - /// When [`kvm_ioctls::VcpuFd::get_tsc_khz`] errrors. + /// When [`kvm_ioctls::VcpuFd::get_tsc_khz`] errors. pub fn get_tsc_khz(&self) -> Result { let res = self.fd.get_tsc_khz()?; Ok(res) @@ -321,7 +389,7 @@ impl KvmVcpu { .filter(|msr| msr.index == MSR_IA32_TSC_DEADLINE && msr.data == 0) .for_each(|msr| { warn!( - "MSR_IA32_TSC_DEADLINE is 0, replacing with {:x}.", + "MSR_IA32_TSC_DEADLINE is 0, replacing with {:#x}.", tsc_value ); msr.data = tsc_value; @@ -409,7 +477,7 @@ impl KvmVcpu { /// # Arguments /// /// * `msr_index_iter`: Iterator over MSR indices. - /// * `chunk_size`: Lenght of a chunk. + /// * `chunk_size`: Length of a chunk. /// /// # Errors /// @@ -436,7 +504,7 @@ impl KvmVcpu { .map_err(KvmVcpuError::VcpuGetMsrs)?; // GET_MSRS returns a number of successfully set msrs. // If number of set msrs is not equal to the length of - // `msrs`, then the value retuned by GET_MSRS can act + // `msrs`, then the value returned by GET_MSRS can act // as an index to the problematic msr. if nmsrs != chunk_size { Err(KvmVcpuError::VcpuGetMsr(msrs.as_slice()[nmsrs].index)) @@ -495,7 +563,7 @@ impl KvmVcpu { .map_err(KvmVcpuError::VcpuGetMpState)?; let regs = self.fd.get_regs().map_err(KvmVcpuError::VcpuGetRegs)?; let sregs = self.fd.get_sregs().map_err(KvmVcpuError::VcpuGetSregs)?; - let xsave = self.fd.get_xsave().map_err(KvmVcpuError::VcpuGetXsave)?; + let xsave = self.get_xsave()?; let xcrs = self.fd.get_xcrs().map_err(KvmVcpuError::VcpuGetXcrs)?; let debug_regs = self .fd @@ -553,7 +621,7 @@ impl KvmVcpu { // in the state. If they are different, we need to // scale the TSC to the freq found in the state. // We accept values within a tolerance of 250 parts - // per million beacuse it is common for TSC frequency + // per million because it is common for TSC frequency // to differ due to calibration at boot time. let diff = (i64::from(self.get_tsc_khz()?) - i64::from(state_tsc_freq)).abs(); // Cannot overflow since u32::MAX * 250 < i64::MAX @@ -600,9 +668,17 @@ impl KvmVcpu { self.fd .set_sregs(&state.sregs) .map_err(KvmVcpuError::VcpuSetSregs)?; - self.fd - .set_xsave(&state.xsave) - .map_err(KvmVcpuError::VcpuSetXsave)?; + // SAFETY: Safe unless the snapshot is corrupted. + unsafe { + // kvm-ioctl's `set_xsave2()` can be called even on kernel versions not supporting + // `KVM_CAP_XSAVE2`, because it internally calls `KVM_SET_XSAVE` API that was extended + // by Linux kernel. Thus, `KVM_SET_XSAVE2` API does not exist as a KVM interface. + // However, kvm-ioctl added `set_xsave2()` to allow users to pass `Xsave` instead of the + // older `kvm_xsave`. + self.fd + .set_xsave2(&state.xsave) + .map_err(KvmVcpuError::VcpuSetXsave)?; + } self.fd .set_xcrs(&state.xcrs) .map_err(KvmVcpuError::VcpuSetXcrs)?; @@ -629,7 +705,7 @@ impl Peripherals { /// Runs the vCPU in KVM context and handles the kvm exit reason. /// /// Returns error or enum specifying whether emulation was handled or interrupted. - pub fn run_arch_emulation(&self, exit: VcpuExit) -> Result { + pub fn run_arch_emulation(&self, exit: VcpuExit) -> Result { match exit { VcpuExit::IoIn(addr, data) => { if let Some(pio_bus) = &self.pio_bus { @@ -652,7 +728,7 @@ impl Peripherals { // TODO: Are we sure we want to finish running a vcpu upon // receiving a vm exit that is not necessarily an error? error!("Unexpected exit reason on vcpu run: {:?}", unexpected_exit); - Err(super::VcpuError::UnhandledKvmExit(format!( + Err(VcpuError::UnhandledKvmExit(format!( "{:?}", unexpected_exit ))) @@ -683,7 +759,7 @@ pub struct VcpuState { /// Xcrs. pub xcrs: kvm_xcrs, /// Xsave. - pub xsave: kvm_xsave, + pub xsave: Xsave, /// Tsc khz. pub tsc_khz: Option, } @@ -715,20 +791,21 @@ impl Debug for VcpuState { mod tests { #![allow(clippy::undocumented_unsafe_blocks)] - use std::os::unix::io::AsRawFd; - use kvm_bindings::kvm_msr_entry; - use kvm_ioctls::{Cap, Kvm}; + use kvm_ioctls::Cap; + use vm_memory::GuestAddress; use super::*; + use crate::arch::BootProtocol; use crate::arch::x86_64::cpu_model::CpuModel; use crate::cpu_config::templates::{ CpuConfiguration, CpuTemplateType, CustomCpuTemplate, GetCpuTemplate, GuestConfigError, StaticCpuTemplate, }; use crate::cpu_config::x86_64::cpuid::{Cpuid, CpuidEntry, CpuidKey}; - use crate::vstate::vm::tests::setup_vm; + use crate::vstate::kvm::Kvm; use crate::vstate::vm::Vm; + use crate::vstate::vm::tests::{setup_vm, setup_vm_with_memory}; impl Default for VcpuState { fn default() -> Self { @@ -742,17 +819,17 @@ mod tests { sregs: Default::default(), vcpu_events: Default::default(), xcrs: Default::default(), - xsave: Default::default(), + xsave: Xsave::new(0).unwrap(), tsc_khz: Some(0), } } } - fn setup_vcpu(mem_size: usize) -> (Vm, KvmVcpu, GuestMemoryMmap) { - let (vm, vm_mem) = setup_vm(mem_size); + fn setup_vcpu(mem_size: usize) -> (Kvm, Vm, KvmVcpu) { + let (kvm, vm) = setup_vm_with_memory(mem_size); vm.setup_irqchip().unwrap(); let vcpu = KvmVcpu::new(0, &vm).unwrap(); - (vm, vcpu, vm_mem) + (kvm, vm, vcpu) } fn is_at_least_cascade_lake() -> bool { @@ -767,11 +844,11 @@ mod tests { } fn create_vcpu_config( - vm: &Vm, + kvm: &Kvm, vcpu: &KvmVcpu, template: &CustomCpuTemplate, ) -> Result { - let cpuid = Cpuid::try_from(vm.supported_cpuid().clone()) + let cpuid = Cpuid::try_from(kvm.supported_cpuid.clone()) .map_err(GuestConfigError::CpuidFromKvmCpuid)?; let msrs = vcpu .get_msrs(template.msr_index_iter()) @@ -787,23 +864,33 @@ mod tests { #[test] fn test_configure_vcpu() { - let (vm, mut vcpu, vm_mem) = setup_vcpu(0x10000); + let (kvm, vm, mut vcpu) = setup_vcpu(0x10000); - let vcpu_config = create_vcpu_config(&vm, &vcpu, &CustomCpuTemplate::default()).unwrap(); + let vcpu_config = create_vcpu_config(&kvm, &vcpu, &CustomCpuTemplate::default()).unwrap(); assert_eq!( - vcpu.configure(&vm_mem, GuestAddress(0), &vcpu_config,), + vcpu.configure( + vm.guest_memory(), + EntryPoint { + entry_addr: GuestAddress(0), + protocol: BootProtocol::LinuxBoot, + }, + &vcpu_config, + ), Ok(()) ); - let try_configure = |vm: &Vm, vcpu: &mut KvmVcpu, template| -> bool { + let try_configure = |kvm: &Kvm, vcpu: &mut KvmVcpu, template| -> bool { let cpu_template = Some(CpuTemplateType::Static(template)); let template = cpu_template.get_cpu_template(); match template { - Ok(template) => match create_vcpu_config(vm, vcpu, &template) { + Ok(template) => match create_vcpu_config(kvm, vcpu, &template) { Ok(config) => vcpu .configure( - &vm_mem, - GuestAddress(crate::arch::get_kernel_start()), + vm.guest_memory(), + EntryPoint { + entry_addr: GuestAddress(crate::arch::get_kernel_start()), + protocol: BootProtocol::LinuxBoot, + }, &config, ) .is_ok(), @@ -814,19 +901,19 @@ mod tests { }; // Test configure while using the T2 template. - let t2_res = try_configure(&vm, &mut vcpu, StaticCpuTemplate::T2); + let t2_res = try_configure(&kvm, &mut vcpu, StaticCpuTemplate::T2); // Test configure while using the C3 template. - let c3_res = try_configure(&vm, &mut vcpu, StaticCpuTemplate::C3); + let c3_res = try_configure(&kvm, &mut vcpu, StaticCpuTemplate::C3); // Test configure while using the T2S template. - let t2s_res = try_configure(&vm, &mut vcpu, StaticCpuTemplate::T2S); + let t2s_res = try_configure(&kvm, &mut vcpu, StaticCpuTemplate::T2S); // Test configure while using the T2CL template. - let t2cl_res = try_configure(&vm, &mut vcpu, StaticCpuTemplate::T2CL); + let t2cl_res = try_configure(&kvm, &mut vcpu, StaticCpuTemplate::T2CL); // Test configure while using the T2S template. - let t2a_res = try_configure(&vm, &mut vcpu, StaticCpuTemplate::T2A); + let t2a_res = try_configure(&kvm, &mut vcpu, StaticCpuTemplate::T2A); match &cpuid::common::get_vendor_id_from_host().unwrap() { cpuid::VENDOR_ID_INTEL => { @@ -859,8 +946,8 @@ mod tests { #[test] fn test_vcpu_cpuid_restore() { - let (vm, vcpu, _mem) = setup_vcpu(0x10000); - vcpu.fd.set_cpuid2(vm.supported_cpuid()).unwrap(); + let (kvm, _, vcpu) = setup_vcpu(0x10000); + vcpu.fd.set_cpuid2(&kvm.supported_cpuid).unwrap(); // Mutate the CPUID. // Leaf 0x3 / EAX that is an unused (reserved to be accurate) register, so it's harmless. @@ -874,10 +961,10 @@ mod tests { // Restore the state into the existing vcpu. let result1 = vcpu.restore_state(&state); assert!(result1.is_ok(), "{}", result1.unwrap_err()); - unsafe { libc::close(vcpu.fd.as_raw_fd()) }; + drop(vcpu); // Restore the state into a new vcpu. - let (_vm, vcpu, _mem) = setup_vcpu(0x10000); + let (_, _vm, vcpu) = setup_vcpu(0x10000); let result2 = vcpu.restore_state(&state); assert!(result2.is_ok(), "{}", result2.unwrap_err()); @@ -897,17 +984,24 @@ mod tests { #[test] fn test_empty_cpuid_entries_removed() { // Test that `get_cpuid()` removes zeroed empty entries from the `KVM_GET_CPUID2` result. - let (vm, mut vcpu, vm_mem) = setup_vcpu(0x10000); + let (kvm, vm, mut vcpu) = setup_vcpu(0x10000); let vcpu_config = VcpuConfig { vcpu_count: 1, smt: false, cpu_config: CpuConfiguration { - cpuid: Cpuid::try_from(vm.supported_cpuid().clone()).unwrap(), + cpuid: Cpuid::try_from(kvm.supported_cpuid.clone()).unwrap(), msrs: BTreeMap::new(), }, }; - vcpu.configure(&vm_mem, GuestAddress(0), &vcpu_config) - .unwrap(); + vcpu.configure( + vm.guest_memory(), + EntryPoint { + entry_addr: GuestAddress(0), + protocol: BootProtocol::LinuxBoot, + }, + &vcpu_config, + ) + .unwrap(); // Invalid entries filled with 0 should not exist. let cpuid = vcpu.get_cpuid().unwrap(); @@ -948,7 +1042,7 @@ mod tests { // Since `KVM_SET_CPUID2` has not been called before vcpu configuration, all leaves should // be filled with zero. Therefore, `KvmVcpu::dump_cpu_config()` should fail with CPUID type // conversion error due to the lack of brand string info in leaf 0x0. - let (_, vcpu, _) = setup_vcpu(0x10000); + let (_, _, vcpu) = setup_vcpu(0x10000); match vcpu.dump_cpu_config() { Err(KvmVcpuError::ConvertCpuidType(_)) => (), Err(err) => panic!("Unexpected error: {err}"), @@ -959,17 +1053,25 @@ mod tests { #[test] fn test_dump_cpu_config_with_configured_vcpu() { // Test `dump_cpu_config()` after vcpu configuration. - let (vm, mut vcpu, vm_mem) = setup_vcpu(0x10000); + let (kvm, vm, mut vcpu) = setup_vcpu(0x10000); let vcpu_config = VcpuConfig { vcpu_count: 1, smt: false, cpu_config: CpuConfiguration { - cpuid: Cpuid::try_from(vm.supported_cpuid().clone()).unwrap(), + cpuid: Cpuid::try_from(kvm.supported_cpuid.clone()).unwrap(), msrs: BTreeMap::new(), }, }; - vcpu.configure(&vm_mem, GuestAddress(0), &vcpu_config) - .unwrap(); + + vcpu.configure( + vm.guest_memory(), + EntryPoint { + entry_addr: GuestAddress(0), + protocol: BootProtocol::LinuxBoot, + }, + &vcpu_config, + ) + .unwrap(); vcpu.dump_cpu_config().unwrap(); } @@ -978,7 +1080,7 @@ mod tests { fn test_is_tsc_scaling_required() { // Test `is_tsc_scaling_required` as if it were on the same // CPU model as the one in the snapshot state. - let (_vm, vcpu, _) = setup_vcpu(0x1000); + let (_, _, vcpu) = setup_vcpu(0x1000); { // The frequency difference is within tolerance. @@ -989,9 +1091,11 @@ mod tests { / u32::try_from(TSC_KHZ_TOL_DENOMINATOR).unwrap() / 2, ); - assert!(!vcpu - .is_tsc_scaling_required(state.tsc_khz.unwrap()) - .unwrap()); + assert!( + !vcpu + .is_tsc_scaling_required(state.tsc_khz.unwrap()) + .unwrap() + ); } { @@ -1003,9 +1107,10 @@ mod tests { / u32::try_from(TSC_KHZ_TOL_DENOMINATOR).unwrap() * 2, ); - assert!(vcpu - .is_tsc_scaling_required(state.tsc_khz.unwrap()) - .unwrap()); + assert!( + vcpu.is_tsc_scaling_required(state.tsc_khz.unwrap()) + .unwrap() + ); } { @@ -1017,7 +1122,7 @@ mod tests { #[test] fn test_set_tsc() { - let (vm, vcpu, _) = setup_vcpu(0x1000); + let (kvm, _, vcpu) = setup_vcpu(0x1000); let mut state = vcpu.save_state().unwrap(); state.tsc_khz = Some( state.tsc_khz.unwrap() @@ -1026,9 +1131,9 @@ mod tests { * 2, ); - if vm.fd().check_extension(Cap::TscControl) { + if kvm.fd.check_extension(Cap::TscControl) { vcpu.set_tsc_khz(state.tsc_khz.unwrap()).unwrap(); - if vm.fd().check_extension(Cap::GetTscKhz) { + if kvm.fd.check_extension(Cap::GetTscKhz) { assert_eq!(vcpu.get_tsc_khz().ok(), state.tsc_khz); } else { vcpu.get_tsc_khz().unwrap_err(); @@ -1042,7 +1147,7 @@ mod tests { fn test_get_msrs_with_msrs_to_save() { // Test `get_msrs()` with the MSR indices that should be serialized into snapshots. // The MSR indices should be valid and this test should succeed. - let (_, vcpu, _) = setup_vcpu(0x1000); + let (_, _, vcpu) = setup_vcpu(0x1000); vcpu.get_msrs(vcpu.msrs_to_save.iter().copied()).unwrap(); } @@ -1050,7 +1155,7 @@ mod tests { fn test_get_msrs_with_msrs_to_dump() { // Test `get_msrs()` with the MSR indices that should be dumped. // All the MSR indices should be valid and the call should succeed. - let (_, vcpu, _) = setup_vcpu(0x1000); + let (_, _, vcpu) = setup_vcpu(0x1000); let kvm = kvm_ioctls::Kvm::new().unwrap(); let msrs_to_dump = crate::arch::x86_64::msr::get_msrs_to_dump(&kvm).unwrap(); @@ -1063,7 +1168,7 @@ mod tests { // Test `get_msrs()` with unsupported MSR indices. This should return `VcpuGetMsr` error // that happens when `KVM_GET_MSRS` fails to populate MSR values in the middle and exits. // Currently, MSR indices 2..=4 are not listed as supported MSRs. - let (_, vcpu, _) = setup_vcpu(0x1000); + let (_, _, vcpu) = setup_vcpu(0x1000); let msr_index_list: Vec = vec![2, 3, 4]; match vcpu.get_msrs(msr_index_list.iter().copied()) { Err(KvmVcpuError::VcpuGetMsr(_)) => (), @@ -1169,13 +1274,11 @@ mod tests { #[test] fn test_get_msr_chunks_preserved_order() { // Regression test for #4666 - - let kvm = Kvm::new().unwrap(); - let vm = Vm::new(Vec::new()).unwrap(); + let (_, vm) = setup_vm(); let vcpu = KvmVcpu::new(0, &vm).unwrap(); // The list of supported MSR indices, in the order they were returned by KVM - let msrs_to_save = crate::arch::x86_64::msr::get_msrs_to_save(&kvm).unwrap(); + let msrs_to_save = vm.msrs_to_save(); // The MSRs after processing. The order should be identical to the one returned by KVM, with // the exception of deferred MSRs, which should be moved to the end (but show up in the same // order as they are listed in [`DEFERRED_MSRS`]. @@ -1188,7 +1291,6 @@ mod tests { .flat_map(|chunk| chunk.as_slice().iter()) .zip( msrs_to_save - .as_slice() .iter() .filter(|&idx| !DEFERRED_MSRS.contains(idx)) .chain(DEFERRED_MSRS.iter()), diff --git a/src/vmm/src/arch/x86_64/vm.rs b/src/vmm/src/arch/x86_64/vm.rs new file mode 100644 index 00000000000..e84b4338e35 --- /dev/null +++ b/src/vmm/src/arch/x86_64/vm.rs @@ -0,0 +1,314 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt; + +use kvm_bindings::{ + KVM_CLOCK_TSC_STABLE, KVM_IRQCHIP_IOAPIC, KVM_IRQCHIP_PIC_MASTER, KVM_IRQCHIP_PIC_SLAVE, + KVM_PIT_SPEAKER_DUMMY, MsrList, kvm_clock_data, kvm_irqchip, kvm_pit_config, kvm_pit_state2, +}; +use kvm_ioctls::Cap; +use serde::{Deserialize, Serialize}; + +use crate::arch::x86_64::msr::MsrError; +use crate::utils::u64_to_usize; +use crate::vstate::memory::{GuestMemoryExtension, GuestMemoryState}; +use crate::vstate::vm::{VmCommon, VmError}; + +/// Error type for [`Vm::restore_state`] +#[allow(missing_docs)] +#[cfg(target_arch = "x86_64")] +#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] +pub enum ArchVmError { + /// Failed to check KVM capability (0): {1} + CheckCapability(Cap, kvm_ioctls::Error), + /// Set PIT2 error: {0} + SetPit2(kvm_ioctls::Error), + /// Set clock error: {0} + SetClock(kvm_ioctls::Error), + /// Set IrqChipPicMaster error: {0} + SetIrqChipPicMaster(kvm_ioctls::Error), + /// Set IrqChipPicSlave error: {0} + SetIrqChipPicSlave(kvm_ioctls::Error), + /// Set IrqChipIoAPIC error: {0} + SetIrqChipIoAPIC(kvm_ioctls::Error), + /// Failed to get KVM vm pit state: {0} + VmGetPit2(kvm_ioctls::Error), + /// Failed to get KVM vm clock: {0} + VmGetClock(kvm_ioctls::Error), + /// Failed to get KVM vm irqchip: {0} + VmGetIrqChip(kvm_ioctls::Error), + /// Failed to set KVM vm irqchip: {0} + VmSetIrqChip(kvm_ioctls::Error), + /// Failed to get MSR index list to save into snapshots: {0} + GetMsrsToSave(MsrError), + /// Failed during KVM_SET_TSS_ADDRESS: {0} + SetTssAddress(kvm_ioctls::Error), +} + +/// Structure representing the current architecture's understand of what a "virtual machine" is. +#[derive(Debug)] +pub struct ArchVm { + /// Architecture independent parts of a vm + pub common: VmCommon, + msrs_to_save: MsrList, + /// Size in bytes requiring to hold the dynamically-sized `kvm_xsave` struct. + /// + /// `None` if `KVM_CAP_XSAVE2` not supported. + xsave2_size: Option, +} + +impl ArchVm { + /// Create a new `Vm` struct. + pub fn new(kvm: &crate::vstate::kvm::Kvm) -> Result { + let common = Self::create_common(kvm)?; + + let msrs_to_save = kvm.msrs_to_save().map_err(ArchVmError::GetMsrsToSave)?; + + // `KVM_CAP_XSAVE2` was introduced to support dynamically-sized XSTATE buffer in kernel + // v5.17. `KVM_GET_EXTENSION(KVM_CAP_XSAVE2)` returns the required size in byte if + // supported; otherwise returns 0. + // https://github.com/torvalds/linux/commit/be50b2065dfa3d88428fdfdc340d154d96bf6848 + // + // Cache the value in order not to call it at each vCPU creation. + let xsave2_size = match common.fd.check_extension_int(Cap::Xsave2) { + // Catch all negative values just in case although the possible negative return value + // of ioctl() is only -1. + ..=-1 => { + return Err(VmError::Arch(ArchVmError::CheckCapability( + Cap::Xsave2, + vmm_sys_util::errno::Error::last(), + ))); + } + 0 => None, + // SAFETY: Safe because negative values are handled above. + ret => Some(usize::try_from(ret).unwrap()), + }; + + common + .fd + .set_tss_address(u64_to_usize(crate::arch::x86_64::layout::KVM_TSS_ADDRESS)) + .map_err(ArchVmError::SetTssAddress)?; + + Ok(ArchVm { + common, + msrs_to_save, + xsave2_size, + }) + } + + /// Pre-vCPU creation setup. + pub fn arch_pre_create_vcpus(&mut self, _: u8) -> Result<(), ArchVmError> { + // For x86_64 we need to create the interrupt controller before calling `KVM_CREATE_VCPUS` + self.setup_irqchip() + } + + /// Post-vCPU creation setup. + pub fn arch_post_create_vcpus(&mut self, _: u8) -> Result<(), ArchVmError> { + Ok(()) + } + + /// Restores the KVM VM state. + /// + /// # Errors + /// + /// When: + /// - [`kvm_ioctls::VmFd::set_pit`] errors. + /// - [`kvm_ioctls::VmFd::set_clock`] errors. + /// - [`kvm_ioctls::VmFd::set_irqchip`] errors. + /// - [`kvm_ioctls::VmFd::set_irqchip`] errors. + /// - [`kvm_ioctls::VmFd::set_irqchip`] errors. + pub fn restore_state(&mut self, state: &VmState) -> Result<(), ArchVmError> { + self.fd() + .set_pit2(&state.pitstate) + .map_err(ArchVmError::SetPit2)?; + self.fd() + .set_clock(&state.clock) + .map_err(ArchVmError::SetClock)?; + self.fd() + .set_irqchip(&state.pic_master) + .map_err(ArchVmError::SetIrqChipPicMaster)?; + self.fd() + .set_irqchip(&state.pic_slave) + .map_err(ArchVmError::SetIrqChipPicSlave)?; + self.fd() + .set_irqchip(&state.ioapic) + .map_err(ArchVmError::SetIrqChipIoAPIC)?; + Ok(()) + } + + /// Creates the irq chip and an in-kernel device model for the PIT. + pub fn setup_irqchip(&self) -> Result<(), ArchVmError> { + self.fd() + .create_irq_chip() + .map_err(ArchVmError::VmSetIrqChip)?; + // We need to enable the emulation of a dummy speaker port stub so that writing to port 0x61 + // (i.e. KVM_SPEAKER_BASE_ADDRESS) does not trigger an exit to user space. + let pit_config = kvm_pit_config { + flags: KVM_PIT_SPEAKER_DUMMY, + ..Default::default() + }; + self.fd() + .create_pit2(pit_config) + .map_err(ArchVmError::VmSetIrqChip) + } + + /// Saves and returns the Kvm Vm state. + pub fn save_state(&self) -> Result { + let pitstate = self.fd().get_pit2().map_err(ArchVmError::VmGetPit2)?; + + let mut clock = self.fd().get_clock().map_err(ArchVmError::VmGetClock)?; + // This bit is not accepted in SET_CLOCK, clear it. + clock.flags &= !KVM_CLOCK_TSC_STABLE; + + let mut pic_master = kvm_irqchip { + chip_id: KVM_IRQCHIP_PIC_MASTER, + ..Default::default() + }; + self.fd() + .get_irqchip(&mut pic_master) + .map_err(ArchVmError::VmGetIrqChip)?; + + let mut pic_slave = kvm_irqchip { + chip_id: KVM_IRQCHIP_PIC_SLAVE, + ..Default::default() + }; + self.fd() + .get_irqchip(&mut pic_slave) + .map_err(ArchVmError::VmGetIrqChip)?; + + let mut ioapic = kvm_irqchip { + chip_id: KVM_IRQCHIP_IOAPIC, + ..Default::default() + }; + self.fd() + .get_irqchip(&mut ioapic) + .map_err(ArchVmError::VmGetIrqChip)?; + + Ok(VmState { + memory: self.common.guest_memory.describe(), + pitstate, + clock, + pic_master, + pic_slave, + ioapic, + }) + } + + /// Gets the list of MSRs to save when creating snapshots + pub fn msrs_to_save(&self) -> &[u32] { + self.msrs_to_save.as_slice() + } + + /// Gets the size (in bytes) of the `kvm_xsave` struct. + pub fn xsave2_size(&self) -> Option { + self.xsave2_size + } +} + +#[derive(Default, Deserialize, Serialize)] +/// Structure holding VM kvm state. +pub struct VmState { + /// guest memory state + pub memory: GuestMemoryState, + pitstate: kvm_pit_state2, + clock: kvm_clock_data, + // TODO: rename this field to adopt inclusive language once Linux updates it, too. + pic_master: kvm_irqchip, + // TODO: rename this field to adopt inclusive language once Linux updates it, too. + pic_slave: kvm_irqchip, + ioapic: kvm_irqchip, +} + +impl fmt::Debug for VmState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("VmState") + .field("pitstate", &self.pitstate) + .field("clock", &self.clock) + .field("pic_master", &"?") + .field("pic_slave", &"?") + .field("ioapic", &"?") + .finish() + } +} + +#[cfg(test)] +mod tests { + use kvm_bindings::{ + KVM_CLOCK_TSC_STABLE, KVM_IRQCHIP_IOAPIC, KVM_IRQCHIP_PIC_MASTER, KVM_IRQCHIP_PIC_SLAVE, + KVM_PIT_SPEAKER_DUMMY, + }; + + use crate::snapshot::Snapshot; + use crate::vstate::vm::VmState; + use crate::vstate::vm::tests::{setup_vm, setup_vm_with_memory}; + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_vm_save_restore_state() { + let (_, vm) = setup_vm(); + // Irqchips, clock and pitstate are not configured so trying to save state should fail. + vm.save_state().unwrap_err(); + + let (_, vm) = setup_vm_with_memory(0x1000); + vm.setup_irqchip().unwrap(); + + let vm_state = vm.save_state().unwrap(); + assert_eq!( + vm_state.pitstate.flags | KVM_PIT_SPEAKER_DUMMY, + KVM_PIT_SPEAKER_DUMMY + ); + assert_eq!(vm_state.clock.flags & KVM_CLOCK_TSC_STABLE, 0); + assert_eq!(vm_state.pic_master.chip_id, KVM_IRQCHIP_PIC_MASTER); + assert_eq!(vm_state.pic_slave.chip_id, KVM_IRQCHIP_PIC_SLAVE); + assert_eq!(vm_state.ioapic.chip_id, KVM_IRQCHIP_IOAPIC); + + let (_, mut vm) = setup_vm_with_memory(0x1000); + vm.setup_irqchip().unwrap(); + + vm.restore_state(&vm_state).unwrap(); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_vm_save_restore_state_bad_irqchip() { + use kvm_bindings::KVM_NR_IRQCHIPS; + + let (_, vm) = setup_vm_with_memory(0x1000); + vm.setup_irqchip().unwrap(); + let mut vm_state = vm.save_state().unwrap(); + + let (_, mut vm) = setup_vm_with_memory(0x1000); + vm.setup_irqchip().unwrap(); + + // Try to restore an invalid PIC Master chip ID + let orig_master_chip_id = vm_state.pic_master.chip_id; + vm_state.pic_master.chip_id = KVM_NR_IRQCHIPS; + vm.restore_state(&vm_state).unwrap_err(); + vm_state.pic_master.chip_id = orig_master_chip_id; + + // Try to restore an invalid PIC Slave chip ID + let orig_slave_chip_id = vm_state.pic_slave.chip_id; + vm_state.pic_slave.chip_id = KVM_NR_IRQCHIPS; + vm.restore_state(&vm_state).unwrap_err(); + vm_state.pic_slave.chip_id = orig_slave_chip_id; + + // Try to restore an invalid IOPIC chip ID + vm_state.ioapic.chip_id = KVM_NR_IRQCHIPS; + vm.restore_state(&vm_state).unwrap_err(); + } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_vmstate_serde() { + let mut snapshot_data = vec![0u8; 10000]; + + let (_, mut vm) = setup_vm_with_memory(0x1000); + vm.setup_irqchip().unwrap(); + let state = vm.save_state().unwrap(); + Snapshot::serialize(&mut snapshot_data.as_mut_slice(), &state).unwrap(); + let restored_state: VmState = Snapshot::deserialize(&mut snapshot_data.as_slice()).unwrap(); + + vm.restore_state(&restored_state).unwrap(); + } +} diff --git a/src/vmm/src/arch/x86_64/xstate.rs b/src/vmm/src/arch/x86_64/xstate.rs new file mode 100644 index 00000000000..78e509dc819 --- /dev/null +++ b/src/vmm/src/arch/x86_64/xstate.rs @@ -0,0 +1,136 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use vmm_sys_util::syscall::SyscallReturnCode; + +use crate::arch::x86_64::generated::arch_prctl; + +const INTEL_AMX_MASK: u64 = 1u64 << arch_prctl::ARCH_XCOMP_TILEDATA; + +/// Errors assocaited with x86_64's dynamic XSAVE state features. +#[derive(Debug, thiserror::Error, displaydoc::Display)] +pub enum XstateError { + /// Failed to get supported XSTATE features: {0} + GetSupportedFeatures(std::io::Error), + /// Failed to request permission for XSTATE feature ({0}): {1} + RequestFeaturePermission(u32, std::io::Error), +} + +/// Request permission for all dynamic XSTATE features. +/// +/// Some XSTATE features are not permitted by default, because they may require a larger area to +/// save their states than the tranditional 4096-byte area. Instead, the permission for them can be +/// requested via arch_prctl(). +/// https://github.com/torvalds/linux/blob/master/Documentation/arch/x86/xstate.rst +/// +/// Firecracker requests permission for them by default if available in order to retrieve the +/// full supported feature set via KVM_GET_SUPPORTED_CPUID. +/// https://docs.kernel.org/virt/kvm/api.html#kvm-get-supported-cpuid +/// +/// Note that requested features can be masked by a CPU template. +pub fn request_dynamic_xstate_features() -> Result<(), XstateError> { + let supported_xfeatures = + match get_supported_xfeatures().map_err(XstateError::GetSupportedFeatures)? { + Some(supported_xfeatures) => supported_xfeatures, + // Exit early if dynamic XSTATE feature enabling is not supported on the kernel. + None => return Ok(()), + }; + + // Intel AMX's TILEDATA + // + // Unless requested, on kernels prior to v6.4, KVM_GET_SUPPORTED_CPUID returns an + // inconsistent state where TILECFG is set but TILEDATA isn't. Such a half-enabled state + // causes guest crash during boot because a guest calls XSETBV instruction with all + // XSAVE feature bits enumerated on CPUID and XSETBV only accepts either of both Intel + // AMX bits enabled or disabled; otherwise resulting in general protection fault. + if supported_xfeatures & INTEL_AMX_MASK == INTEL_AMX_MASK { + request_xfeature_permission(arch_prctl::ARCH_XCOMP_TILEDATA).map_err(|err| { + XstateError::RequestFeaturePermission(arch_prctl::ARCH_XCOMP_TILEDATA, err) + })?; + } + + Ok(()) +} + +/// Get supported XSTATE features +/// +/// Returns Ok(None) if dynamic XSTATE feature enabling is not supported. +fn get_supported_xfeatures() -> Result, std::io::Error> { + let mut supported_xfeatures: u64 = 0; + + // SAFETY: Safe because the third input (`addr`) is a valid `c_ulong` pointer. + // https://man7.org/linux/man-pages/man2/arch_prctl.2.html + match SyscallReturnCode(unsafe { + libc::syscall( + libc::SYS_arch_prctl, + arch_prctl::ARCH_GET_XCOMP_SUPP, + &mut supported_xfeatures as *mut libc::c_ulong, + ) + }) + .into_empty_result() + { + Ok(()) => Ok(Some(supported_xfeatures)), + // EINVAL is returned if the dynamic XSTATE feature enabling is not supported (e.g. kernel + // version prior to v5.17). + // https://github.com/torvalds/linux/commit/980fe2fddcff21937c93532b4597c8ea450346c1 + Err(err) if err.raw_os_error() == Some(libc::EINVAL) => Ok(None), + Err(err) => Err(err), + } +} + +/// Request permission for a dynamic XSTATE feature. +/// +/// This should be called after `get_supported_xfeatures()` that also checks that dynamic XSTATE +/// feature enabling is supported. +fn request_xfeature_permission(xfeature: u32) -> Result<(), std::io::Error> { + // SAFETY: Safe because the third input (`addr`) is a valid `c_ulong` value. + // https://man7.org/linux/man-pages/man2/arch_prctl.2.html + SyscallReturnCode(unsafe { + libc::syscall( + libc::SYS_arch_prctl, + arch_prctl::ARCH_REQ_XCOMP_GUEST_PERM as libc::c_ulong, + xfeature as libc::c_ulong, + ) + }) + .into_empty_result() +} + +#[cfg(test)] +mod tests { + use super::*; + + // Get permitted XSTATE features. + fn get_permitted_xstate_features() -> Result { + let mut permitted_xfeatures: u64 = 0; + // SAFETY: Safe because the third input (`addr`) is a valid `c_ulong` pointer. + match SyscallReturnCode(unsafe { + libc::syscall( + libc::SYS_arch_prctl, + arch_prctl::ARCH_GET_XCOMP_GUEST_PERM, + &mut permitted_xfeatures as *mut libc::c_ulong, + ) + }) + .into_empty_result() + { + Ok(()) => Ok(permitted_xfeatures), + Err(err) => Err(err), + } + } + + #[test] + fn test_request_xstate_feature_permission() { + request_dynamic_xstate_features().unwrap(); + + let supported_xfeatures = match get_supported_xfeatures().unwrap() { + Some(supported_xfeatures) => supported_xfeatures, + // Nothing to test if dynamic XSTATE feature enabling is not supported on the kernel. + None => return, + }; + + // Check each dynamic feature is enabled. (currently only Intel AMX TILEDATA) + if supported_xfeatures & INTEL_AMX_MASK == INTEL_AMX_MASK { + let permitted_xfeatures = get_permitted_xstate_features().unwrap(); + assert_eq!(permitted_xfeatures & INTEL_AMX_MASK, INTEL_AMX_MASK); + } + } +} diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index 1f52fdad063..27ba3e96fa9 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -3,10 +3,8 @@ //! Enables pre-boot setup, instantiation and booting of a Firecracker VMM. -#[cfg(target_arch = "x86_64")] -use std::convert::TryFrom; use std::fmt::Debug; -use std::io::{self, Seek, SeekFrom}; +use std::io; #[cfg(feature = "gdb")] use std::sync::mpsc; use std::sync::{Arc, Mutex}; @@ -14,41 +12,32 @@ use std::sync::{Arc, Mutex}; use event_manager::{MutEventSubscriber, SubscriberOps}; use libc::EFD_NONBLOCK; use linux_loader::cmdline::Cmdline as LoaderKernelCmdline; -#[cfg(target_arch = "x86_64")] -use linux_loader::loader::elf::Elf as Loader; -#[cfg(target_arch = "aarch64")] -use linux_loader::loader::pe::PE as Loader; -use linux_loader::loader::KernelLoader; -use seccompiler::BpfThreadMap; use userfaultfd::Uffd; use utils::time::TimestampUs; -use vm_memory::ReadVolatile; #[cfg(target_arch = "aarch64")] use vm_superio::Rtc; use vm_superio::Serial; use vmm_sys_util::eventfd::EventFd; -#[cfg(target_arch = "x86_64")] -use crate::acpi; -use crate::arch::InitrdConfig; +use crate::arch::{ConfigurationError, configure_system_for_boot, load_kernel}; #[cfg(target_arch = "aarch64")] use crate::construct_kvm_mpidrs; use crate::cpu_config::templates::{ - CpuConfiguration, CustomCpuTemplate, GetCpuTemplate, GetCpuTemplateError, GuestConfigError, - KvmCapability, + GetCpuTemplate, GetCpuTemplateError, GuestConfigError, KvmCapability, }; use crate::device_manager::acpi::ACPIDeviceManager; #[cfg(target_arch = "x86_64")] use crate::device_manager::legacy::PortIODeviceManager; -use crate::device_manager::mmio::MMIODeviceManager; +use crate::device_manager::mmio::{MMIODeviceManager, MmioError}; use crate::device_manager::persist::{ ACPIDeviceManagerConstructorArgs, ACPIDeviceManagerRestoreError, MMIODevManagerConstructorArgs, }; use crate::device_manager::resources::ResourceAllocator; +use crate::devices::BusDevice; use crate::devices::acpi::vmgenid::{VmGenId, VmGenIdError}; -use crate::devices::legacy::serial::SerialOut; #[cfg(target_arch = "aarch64")] use crate::devices::legacy::RTCDevice; +use crate::devices::legacy::serial::SerialOut; use crate::devices::legacy::{EventFdTrigger, SerialEventsWrapper, SerialWrapper}; use crate::devices::virtio::balloon::Balloon; use crate::devices::virtio::block::device::Block; @@ -57,21 +46,21 @@ use crate::devices::virtio::mmio::MmioTransport; use crate::devices::virtio::net::Net; use crate::devices::virtio::rng::Entropy; use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend}; -use crate::devices::BusDevice; #[cfg(feature = "gdb")] use crate::gdb; +use crate::initrd::{InitrdConfig, InitrdError}; use crate::logger::{debug, error}; use crate::persist::{MicrovmState, MicrovmStateError}; use crate::resources::VmResources; +use crate::seccomp::BpfThreadMap; use crate::snapshot::Persist; -use crate::utils::u64_to_usize; -use crate::vmm_config::boot_source::BootConfig; use crate::vmm_config::instance_info::InstanceInfo; -use crate::vmm_config::machine_config::{VmConfig, VmConfigError}; -use crate::vstate::memory::{GuestAddress, GuestMemory, GuestMemoryMmap}; -use crate::vstate::vcpu::{Vcpu, VcpuConfig, VcpuError}; +use crate::vmm_config::machine_config::MachineConfigError; +use crate::vstate::kvm::Kvm; +use crate::vstate::memory::GuestRegionMmap; +use crate::vstate::vcpu::{Vcpu, VcpuError}; use crate::vstate::vm::Vm; -use crate::{device_manager, EventManager, Vmm, VmmError}; +use crate::{EventManager, Vmm, VmmError, device_manager}; /// Errors associated with starting the instance. #[derive(Debug, thiserror::Error, displaydoc::Display)] @@ -81,7 +70,7 @@ pub enum StartMicrovmError { /// Unable to attach the VMGenID device: {0} AttachVmgenidDevice(kvm_ioctls::Error), /// System configuration error: {0} - ConfigureSystem(crate::arch::ConfigurationError), + ConfigureSystem(#[from] ConfigurationError), /// Failed to create guest config: {0} CreateGuestConfig(#[from] GuestConfigError), /// Cannot create network device: {0} @@ -95,18 +84,14 @@ pub enum StartMicrovmError { CreateVMGenID(VmGenIdError), /// Invalid Memory Configuration: {0} GuestMemory(crate::vstate::memory::MemoryError), - /// Cannot load initrd due to an invalid memory configuration. - InitrdLoad, - /// Cannot load initrd due to an invalid image: {0} - InitrdRead(io::Error), + /// Error with initrd initialization: {0}. + Initrd(#[from] InitrdError), /// Internal error while starting microVM: {0} - Internal(VmmError), + Internal(#[from] VmmError), /// Failed to get CPU template: {0} GetCpuTemplate(#[from] GetCpuTemplateError), /// Invalid kernel command line: {0} KernelCmdline(String), - /// Cannot load kernel due to invalid memory configuration or invalid kernel image: {0} - KernelLoader(linux_loader::loader::Error), /// Cannot load command line string: {0} LoadCommandline(linux_loader::loader::Error), /// Cannot start microvm without kernel configuration. @@ -124,14 +109,11 @@ pub enum StartMicrovmError { /// Cannot restore microvm state: {0} RestoreMicrovmState(MicrovmStateError), /// Cannot set vm resources: {0} - SetVmResources(VmConfigError), + SetVmResources(MachineConfigError), /// Cannot create the entropy device: {0} CreateEntropyDevice(crate::devices::virtio::rng::EntropyError), /// Failed to allocate guest resource: {0} AllocateResources(#[from] vm_allocator::Error), - /// Error configuring ACPI: {0} - #[cfg(target_arch = "x86_64")] - Acpi(#[from] crate::acpi::AcpiError), /// Error starting GDB debug session #[cfg(feature = "gdb")] GdbServer(gdb::target::GdbTargetError), @@ -152,26 +134,13 @@ impl std::convert::From for StartMicrovmError { fn create_vmm_and_vcpus( instance_info: &InstanceInfo, event_manager: &mut EventManager, - guest_memory: GuestMemoryMmap, - uffd: Option, - track_dirty_pages: bool, vcpu_count: u8, kvm_capabilities: Vec, -) -> Result<(Vmm, Vec), StartMicrovmError> { - use self::StartMicrovmError::*; - +) -> Result<(Vmm, Vec), VmmError> { + let kvm = Kvm::new(kvm_capabilities)?; // Set up Kvm Vm and register memory regions. // Build custom CPU config if a custom template is provided. - let mut vm = Vm::new(kvm_capabilities) - .map_err(VmmError::Vm) - .map_err(StartMicrovmError::Internal)?; - vm.memory_init(&guest_memory, track_dirty_pages) - .map_err(VmmError::Vm) - .map_err(StartMicrovmError::Internal)?; - - let vcpus_exit_evt = EventFd::new(libc::EFD_NONBLOCK) - .map_err(VmmError::EventFd) - .map_err(Internal)?; + let mut vm = Vm::new(&kvm)?; let resource_allocator = ResourceAllocator::new()?; @@ -181,55 +150,33 @@ fn create_vmm_and_vcpus( // Instantiate ACPI device manager. let acpi_device_manager = ACPIDeviceManager::new(); - // For x86_64 we need to create the interrupt controller before calling `KVM_CREATE_VCPUS` - // while on aarch64 we need to do it the other way around. - #[cfg(target_arch = "x86_64")] - let (vcpus, pio_device_manager) = { - setup_interrupt_controller(&mut vm)?; - let vcpus = create_vcpus(&vm, vcpu_count, &vcpus_exit_evt).map_err(Internal)?; + let (vcpus, vcpus_exit_evt) = vm.create_vcpus(vcpu_count)?; + #[cfg(target_arch = "x86_64")] + let pio_device_manager = { // Make stdout non blocking. set_stdout_nonblocking(); // Serial device setup. - let serial_device = - setup_serial_device(event_manager, std::io::stdin(), io::stdout()).map_err(Internal)?; + let serial_device = setup_serial_device(event_manager, std::io::stdin(), io::stdout())?; // x86_64 uses the i8042 reset event as the Vmm exit event. - let reset_evt = vcpus_exit_evt - .try_clone() - .map_err(VmmError::EventFd) - .map_err(Internal)?; + let reset_evt = vcpus_exit_evt.try_clone().map_err(VmmError::EventFd)?; // create pio dev manager with legacy devices - let pio_device_manager = { - // TODO Remove these unwraps. - let mut pio_dev_mgr = PortIODeviceManager::new(serial_device, reset_evt).unwrap(); - pio_dev_mgr.register_devices(vm.fd()).unwrap(); - pio_dev_mgr - }; - - (vcpus, pio_device_manager) - }; - - // On aarch64, the vCPUs need to be created (i.e call KVM_CREATE_VCPU) before setting up the - // IRQ chip because the `KVM_CREATE_VCPU` ioctl will return error if the IRQCHIP - // was already initialized. - // Search for `kvm_arch_vcpu_create` in arch/arm/kvm/arm.c. - #[cfg(target_arch = "aarch64")] - let vcpus = { - let vcpus = create_vcpus(&vm, vcpu_count, &vcpus_exit_evt).map_err(Internal)?; - setup_interrupt_controller(&mut vm, vcpu_count)?; - vcpus + // TODO Remove these unwraps. + let mut pio_dev_mgr = PortIODeviceManager::new(serial_device, reset_evt).unwrap(); + pio_dev_mgr.register_devices(vm.fd()).unwrap(); + pio_dev_mgr }; let vmm = Vmm { events_observer: Some(std::io::stdin()), instance_info: instance_info.clone(), shutdown_exit_code: None, + kvm, vm, - guest_memory, - uffd, + uffd: None, vcpus_handles: Vec::new(), vcpus_exit_evt, resource_allocator, @@ -259,31 +206,38 @@ pub fn build_microvm_for_boot( let request_ts = TimestampUs::default(); let boot_config = vm_resources - .boot_source_builder() + .boot_source + .builder + .as_ref() .ok_or(MissingKernelConfig)?; let guest_memory = vm_resources .allocate_guest_memory() .map_err(StartMicrovmError::GuestMemory)?; - let entry_addr = load_kernel(boot_config, &guest_memory)?; - let initrd = load_initrd_from_config(boot_config, &guest_memory)?; // Clone the command-line so that a failed boot doesn't pollute the original. #[allow(unused_mut)] let mut boot_cmdline = boot_config.cmdline.clone(); - let cpu_template = vm_resources.vm_config.cpu_template.get_cpu_template()?; + let cpu_template = vm_resources + .machine_config + .cpu_template + .get_cpu_template()?; let (mut vmm, mut vcpus) = create_vmm_and_vcpus( instance_info, event_manager, - guest_memory, - None, - vm_resources.vm_config.track_dirty_pages, - vm_resources.vm_config.vcpu_count, + vm_resources.machine_config.vcpu_count, cpu_template.kvm_capabilities.clone(), )?; + vmm.vm + .register_memory_regions(guest_memory) + .map_err(VmmError::Vm)?; + + let entry_point = load_kernel(&boot_config.kernel_file, vmm.vm.guest_memory())?; + let initrd = InitrdConfig::from_config(boot_config, vmm.vm.guest_memory())?; + #[cfg(feature = "gdb")] let (gdb_tx, gdb_rx) = mpsc::channel(); #[cfg(feature = "gdb")] @@ -329,16 +283,16 @@ pub fn build_microvm_for_boot( } #[cfg(target_arch = "aarch64")] - attach_legacy_devices_aarch64(event_manager, &mut vmm, &mut boot_cmdline).map_err(Internal)?; + attach_legacy_devices_aarch64(event_manager, &mut vmm, &mut boot_cmdline)?; attach_vmgenid_device(&mut vmm)?; configure_system_for_boot( &mut vmm, vcpus.as_mut(), - &vm_resources.vm_config, + &vm_resources.machine_config, &cpu_template, - entry_addr, + entry_point, &initrd, boot_cmdline, )?; @@ -346,9 +300,15 @@ pub fn build_microvm_for_boot( let vmm = Arc::new(Mutex::new(vmm)); #[cfg(feature = "gdb")] - if let Some(gdb_socket_path) = &vm_resources.vm_config.gdb_socket_path { - gdb::gdb_thread(vmm.clone(), vcpu_fds, gdb_rx, entry_addr, gdb_socket_path) - .map_err(GdbServer)?; + if let Some(gdb_socket_path) = &vm_resources.machine_config.gdb_socket_path { + gdb::gdb_thread( + vmm.clone(), + vcpu_fds, + gdb_rx, + entry_point.entry_addr, + gdb_socket_path, + ) + .map_err(GdbServer)?; } else { debug!("No GDB socket provided not starting gdb server."); } @@ -363,20 +323,18 @@ pub fn build_microvm_for_boot( .ok_or_else(|| MissingSeccompFilters("vcpu".to_string()))? .clone(), ) - .map_err(VmmError::VcpuStart) - .map_err(Internal)?; + .map_err(VmmError::VcpuStart)?; // Load seccomp filters for the VMM thread. // Execution panics if filters cannot be loaded, use --no-seccomp if skipping filters // altogether is the desired behaviour. // Keep this as the last step before resuming vcpus. - seccompiler::apply_filter( + crate::seccomp::apply_filter( seccomp_filters .get("vmm") .ok_or_else(|| MissingSeccompFilters("vmm".to_string()))?, ) - .map_err(VmmError::SeccompFilters) - .map_err(Internal)?; + .map_err(VmmError::SeccompFilters)?; event_manager.add_subscriber(vmm.clone()); @@ -401,10 +359,7 @@ pub fn build_and_boot_microvm( debug!("event_end: build microvm for boot"); // The vcpus start off in the `Paused` state, let them run. debug!("event_start: boot microvm"); - vmm.lock() - .unwrap() - .resume_vm() - .map_err(StartMicrovmError::Internal)?; + vmm.lock().unwrap().resume_vm()?; debug!("event_end: boot microvm"); Ok(vmm) } @@ -420,14 +375,14 @@ pub enum BuildMicrovmFromSnapshotError { TscFrequencyNotPresent, #[cfg(target_arch = "x86_64")] /// Could not get TSC to check if TSC scaling was required with the snapshot: {0} - GetTsc(#[from] crate::vstate::vcpu::GetTscError), + GetTsc(#[from] crate::arch::GetTscError), #[cfg(target_arch = "x86_64")] /// Could not set TSC scaling within the snapshot: {0} - SetTsc(#[from] crate::vstate::vcpu::SetTscError), + SetTsc(#[from] crate::arch::SetTscError), /// Failed to restore microVM state: {0} - RestoreState(#[from] crate::vstate::vm::RestoreStateError), + RestoreState(#[from] crate::vstate::vm::ArchVmError), /// Failed to update microVM configuration: {0} - VmUpdateConfig(#[from] VmConfigError), + VmUpdateConfig(#[from] MachineConfigError), /// Failed to restore MMIO device: {0} RestoreMmioDevice(#[from] MicrovmStateError), /// Failed to emulate MMIO serial: {0} @@ -441,7 +396,7 @@ pub enum BuildMicrovmFromSnapshotError { /// Failed to apply VMM secccomp filter as none found. MissingVmmSeccompFilters, /// Failed to apply VMM secccomp filter: {0} - SeccompFiltersInternal(#[from] seccompiler::InstallationError), + SeccompFiltersInternal(#[from] crate::seccomp::InstallationError), /// Failed to restore ACPI device manager: {0} ACPIDeviManager(#[from] ACPIDeviceManagerRestoreError), /// VMGenID update failed: {0} @@ -457,7 +412,7 @@ pub fn build_microvm_from_snapshot( instance_info: &InstanceInfo, event_manager: &mut EventManager, microvm_state: MicrovmState, - guest_memory: GuestMemoryMmap, + guest_memory: Vec, uffd: Option, seccomp_filters: &BpfThreadMap, vm_resources: &mut VmResources, @@ -467,12 +422,16 @@ pub fn build_microvm_from_snapshot( let (mut vmm, mut vcpus) = create_vmm_and_vcpus( instance_info, event_manager, - guest_memory.clone(), - uffd, - vm_resources.vm_config.track_dirty_pages, - vm_resources.vm_config.vcpu_count, - microvm_state.vm_state.kvm_cap_modifiers.clone(), - )?; + vm_resources.machine_config.vcpu_count, + microvm_state.kvm_state.kvm_cap_modifiers.clone(), + ) + .map_err(StartMicrovmError::Internal)?; + + vmm.vm + .register_memory_regions(guest_memory) + .map_err(VmmError::Vm) + .map_err(StartMicrovmError::Internal)?; + vmm.uffd = uffd; #[cfg(target_arch = "x86_64")] { @@ -508,16 +467,17 @@ pub fn build_microvm_from_snapshot( vmm.vm.restore_state(µvm_state.vm_state)?; // Restore the boot source config paths. - vm_resources.set_boot_source_config(microvm_state.vm_info.boot_source); + vm_resources.boot_source.config = microvm_state.vm_info.boot_source; // Restore devices states. let mmio_ctor_args = MMIODevManagerConstructorArgs { - mem: &guest_memory, + mem: vmm.vm.guest_memory(), vm: vmm.vm.fd(), event_manager, resource_allocator: &mut vmm.resource_allocator, vm_resources, instance_id: &instance_info.id, + restored_from_file: vmm.uffd.is_none(), }; vmm.mmio_device_manager = @@ -527,7 +487,7 @@ pub fn build_microvm_from_snapshot( { let acpi_ctor_args = ACPIDeviceManagerConstructorArgs { - mem: &guest_memory, + mem: vmm.vm.guest_memory(), resource_allocator: &mut vmm.resource_allocator, vm: vmm.vm.fd(), }; @@ -557,7 +517,7 @@ pub fn build_microvm_from_snapshot( // Load seccomp filters for the VMM thread. // Keep this as the last step of the building process. - seccompiler::apply_filter( + crate::seccomp::apply_filter( seccomp_filters .get("vmm") .ok_or(BuildMicrovmFromSnapshotError::MissingVmmSeccompFilters)?, @@ -567,115 +527,6 @@ pub fn build_microvm_from_snapshot( Ok(vmm) } -fn load_kernel( - boot_config: &BootConfig, - guest_memory: &GuestMemoryMmap, -) -> Result { - let mut kernel_file = boot_config - .kernel_file - .try_clone() - .map_err(|err| StartMicrovmError::Internal(VmmError::KernelFile(err)))?; - - #[cfg(target_arch = "x86_64")] - let entry_addr = Loader::load::( - guest_memory, - None, - &mut kernel_file, - Some(GuestAddress(crate::arch::get_kernel_start())), - ) - .map_err(StartMicrovmError::KernelLoader)?; - - #[cfg(target_arch = "aarch64")] - let entry_addr = Loader::load::( - guest_memory, - Some(GuestAddress(crate::arch::get_kernel_start())), - &mut kernel_file, - None, - ) - .map_err(StartMicrovmError::KernelLoader)?; - - Ok(entry_addr.kernel_load) -} - -fn load_initrd_from_config( - boot_cfg: &BootConfig, - vm_memory: &GuestMemoryMmap, -) -> Result, StartMicrovmError> { - use self::StartMicrovmError::InitrdRead; - - Ok(match &boot_cfg.initrd_file { - Some(f) => Some(load_initrd( - vm_memory, - &mut f.try_clone().map_err(InitrdRead)?, - )?), - None => None, - }) -} - -/// Loads the initrd from a file into the given memory slice. -/// -/// * `vm_memory` - The guest memory the initrd is written to. -/// * `image` - The initrd image. -/// -/// Returns the result of initrd loading -fn load_initrd( - vm_memory: &GuestMemoryMmap, - image: &mut F, -) -> Result -where - F: ReadVolatile + Seek + Debug, -{ - use self::StartMicrovmError::{InitrdLoad, InitrdRead}; - - let size: usize; - // Get the image size - match image.seek(SeekFrom::End(0)) { - Err(err) => return Err(InitrdRead(err)), - Ok(0) => { - return Err(InitrdRead(io::Error::new( - io::ErrorKind::InvalidData, - "Initrd image seek returned a size of zero", - ))) - } - Ok(s) => size = u64_to_usize(s), - }; - // Go back to the image start - image.seek(SeekFrom::Start(0)).map_err(InitrdRead)?; - - // Get the target address - let address = crate::arch::initrd_load_addr(vm_memory, size).map_err(|_| InitrdLoad)?; - - // Load the image into memory - let mut slice = vm_memory - .get_slice(GuestAddress(address), size) - .map_err(|_| InitrdLoad)?; - - image - .read_exact_volatile(&mut slice) - .map_err(|_| InitrdLoad)?; - - Ok(InitrdConfig { - address: GuestAddress(address), - size, - }) -} - -/// Sets up the irqchip for a x86_64 microVM. -#[cfg(target_arch = "x86_64")] -pub fn setup_interrupt_controller(vm: &mut Vm) -> Result<(), StartMicrovmError> { - vm.setup_irqchip() - .map_err(VmmError::Vm) - .map_err(StartMicrovmError::Internal) -} - -/// Sets up the irqchip for a aarch64 microVM. -#[cfg(target_arch = "aarch64")] -pub fn setup_interrupt_controller(vm: &mut Vm, vcpu_count: u8) -> Result<(), StartMicrovmError> { - vm.setup_irqchip(vcpu_count) - .map_err(VmmError::Vm) - .map_err(StartMicrovmError::Internal) -} - /// Sets up the serial device. pub fn setup_serial_device( event_manager: &mut EventManager, @@ -733,132 +584,6 @@ fn attach_legacy_devices_aarch64( .map_err(VmmError::RegisterMMIODevice) } -fn create_vcpus(vm: &Vm, vcpu_count: u8, exit_evt: &EventFd) -> Result, VmmError> { - let mut vcpus = Vec::with_capacity(vcpu_count as usize); - for cpu_idx in 0..vcpu_count { - let exit_evt = exit_evt.try_clone().map_err(VmmError::EventFd)?; - let vcpu = Vcpu::new(cpu_idx, vm, exit_evt).map_err(VmmError::VcpuCreate)?; - vcpus.push(vcpu); - } - Ok(vcpus) -} - -/// Configures the system for booting Linux. -#[cfg_attr(target_arch = "aarch64", allow(unused))] -pub fn configure_system_for_boot( - vmm: &mut Vmm, - vcpus: &mut [Vcpu], - vm_config: &VmConfig, - cpu_template: &CustomCpuTemplate, - entry_addr: GuestAddress, - initrd: &Option, - boot_cmdline: LoaderKernelCmdline, -) -> Result<(), StartMicrovmError> { - use self::StartMicrovmError::*; - - // Construct the base CpuConfiguration to apply CPU template onto. - #[cfg(target_arch = "x86_64")] - let cpu_config = { - use crate::cpu_config::x86_64::cpuid; - let cpuid = cpuid::Cpuid::try_from(vmm.vm.supported_cpuid().clone()) - .map_err(GuestConfigError::CpuidFromKvmCpuid)?; - let msrs = vcpus[0] - .kvm_vcpu - .get_msrs(cpu_template.msr_index_iter()) - .map_err(GuestConfigError::VcpuIoctl)?; - CpuConfiguration { cpuid, msrs } - }; - - #[cfg(target_arch = "aarch64")] - let cpu_config = { - use crate::arch::aarch64::regs::Aarch64RegisterVec; - use crate::arch::aarch64::vcpu::get_registers; - - for vcpu in vcpus.iter_mut() { - vcpu.kvm_vcpu - .init(&cpu_template.vcpu_features) - .map_err(VmmError::VcpuInit) - .map_err(Internal)?; - } - - let mut regs = Aarch64RegisterVec::default(); - get_registers(&vcpus[0].kvm_vcpu.fd, &cpu_template.reg_list(), &mut regs) - .map_err(GuestConfigError)?; - CpuConfiguration { regs } - }; - - // Apply CPU template to the base CpuConfiguration. - let cpu_config = CpuConfiguration::apply_template(cpu_config, cpu_template)?; - - let vcpu_config = VcpuConfig { - vcpu_count: vm_config.vcpu_count, - smt: vm_config.smt, - cpu_config, - }; - - // Configure vCPUs with normalizing and setting the generated CPU configuration. - for vcpu in vcpus.iter_mut() { - vcpu.kvm_vcpu - .configure(vmm.guest_memory(), entry_addr, &vcpu_config) - .map_err(VmmError::VcpuConfigure) - .map_err(Internal)?; - } - - #[cfg(target_arch = "x86_64")] - { - // Write the kernel command line to guest memory. This is x86_64 specific, since on - // aarch64 the command line will be specified through the FDT. - let cmdline_size = boot_cmdline - .as_cstring() - .map(|cmdline_cstring| cmdline_cstring.as_bytes_with_nul().len())?; - - linux_loader::loader::load_cmdline::( - vmm.guest_memory(), - GuestAddress(crate::arch::x86_64::layout::CMDLINE_START), - &boot_cmdline, - ) - .map_err(LoadCommandline)?; - crate::arch::x86_64::configure_system( - &vmm.guest_memory, - &mut vmm.resource_allocator, - crate::vstate::memory::GuestAddress(crate::arch::x86_64::layout::CMDLINE_START), - cmdline_size, - initrd, - vcpu_config.vcpu_count, - ) - .map_err(ConfigureSystem)?; - - // Create ACPI tables and write them in guest memory - // For the time being we only support ACPI in x86_64 - acpi::create_acpi_tables( - &vmm.guest_memory, - &mut vmm.resource_allocator, - &vmm.mmio_device_manager, - &vmm.acpi_device_manager, - vcpus, - )?; - } - #[cfg(target_arch = "aarch64")] - { - let vcpu_mpidr = vcpus - .iter_mut() - .map(|cpu| cpu.kvm_vcpu.get_mpidr()) - .collect(); - let cmdline = boot_cmdline.as_cstring()?; - crate::arch::aarch64::configure_system( - &vmm.guest_memory, - cmdline, - vcpu_mpidr, - vmm.mmio_device_manager.get_device_info(), - vmm.vm.get_irqchip(), - &vmm.acpi_device_manager.vmgenid, - initrd, - ) - .map_err(ConfigureSystem)?; - } - Ok(()) -} - /// Attaches a VirtioDevice device to the device manager and event manager. fn attach_virtio_device( event_manager: &mut EventManager, @@ -867,13 +592,11 @@ fn attach_virtio_device( device: Arc>, cmdline: &mut LoaderKernelCmdline, is_vhost_user: bool, -) -> Result<(), StartMicrovmError> { - use self::StartMicrovmError::*; - +) -> Result<(), MmioError> { event_manager.add_subscriber(device.clone()); // The device mutex mustn't be locked here otherwise it will deadlock. - let device = MmioTransport::new(vmm.guest_memory().clone(), device, is_vhost_user); + let device = MmioTransport::new(vmm.vm.guest_memory().clone(), device, is_vhost_user); vmm.mmio_device_manager .register_mmio_virtio_for_boot( vmm.vm.fd(), @@ -882,27 +605,23 @@ fn attach_virtio_device( device, cmdline, ) - .map_err(RegisterMmioDevice) .map(|_| ()) } pub(crate) fn attach_boot_timer_device( vmm: &mut Vmm, request_ts: TimestampUs, -) -> Result<(), StartMicrovmError> { - use self::StartMicrovmError::*; - +) -> Result<(), MmioError> { let boot_timer = crate::devices::pseudo::BootTimer::new(request_ts); vmm.mmio_device_manager - .register_mmio_boot_timer(&mut vmm.resource_allocator, boot_timer) - .map_err(RegisterMmioDevice)?; + .register_mmio_boot_timer(&mut vmm.resource_allocator, boot_timer)?; Ok(()) } fn attach_vmgenid_device(vmm: &mut Vmm) -> Result<(), StartMicrovmError> { - let vmgenid = VmGenId::new(&vmm.guest_memory, &mut vmm.resource_allocator) + let vmgenid = VmGenId::new(vmm.vm.guest_memory(), &mut vmm.resource_allocator) .map_err(StartMicrovmError::CreateVMGenID)?; vmm.acpi_device_manager @@ -917,7 +636,7 @@ fn attach_entropy_device( cmdline: &mut LoaderKernelCmdline, entropy_device: &Arc>, event_manager: &mut EventManager, -) -> Result<(), StartMicrovmError> { +) -> Result<(), MmioError> { let id = entropy_device .lock() .expect("Poisoned lock") @@ -945,9 +664,7 @@ fn attach_block_devices<'a, I: Iterator>> + Debug>( let locked = block.lock().expect("Poisoned lock"); if locked.root_device() { match locked.partuuid() { - Some(ref partuuid) => { - cmdline.insert_str(format!("root=PARTUUID={}", partuuid))? - } + Some(partuuid) => cmdline.insert_str(format!("root=PARTUUID={}", partuuid))?, None => cmdline.insert_str("root=/dev/vda")?, } match locked.read_only() { @@ -989,7 +706,7 @@ fn attach_unixsock_vsock_device( cmdline: &mut LoaderKernelCmdline, unix_vsock: &Arc>>, event_manager: &mut EventManager, -) -> Result<(), StartMicrovmError> { +) -> Result<(), MmioError> { let id = String::from(unix_vsock.lock().expect("Poisoned lock").id()); // The device mutex mustn't be locked here otherwise it will deadlock. attach_virtio_device(event_manager, vmm, id, unix_vsock.clone(), cmdline, false) @@ -1000,7 +717,7 @@ fn attach_balloon_device( cmdline: &mut LoaderKernelCmdline, balloon: &Arc>, event_manager: &mut EventManager, -) -> Result<(), StartMicrovmError> { +) -> Result<(), MmioError> { let id = String::from(balloon.lock().expect("Poisoned lock").id()); // The device mutex mustn't be locked here otherwise it will deadlock. attach_virtio_device(event_manager, vmm, id, balloon.clone(), cmdline, false) @@ -1021,8 +738,7 @@ pub(crate) fn set_stdout_nonblocking() { } #[cfg(test)] -pub mod tests { - use std::io::Write; +pub(crate) mod tests { use linux_loader::cmdline::Cmdline; use vmm_sys_util::tempfile::TempFile; @@ -1036,14 +752,15 @@ pub mod tests { use crate::devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_RNG}; use crate::mmds::data_store::{Mmds, MmdsVersion}; use crate::mmds::ns::MmdsNetworkStack; - use crate::test_utils::{arch_mem, single_region_mem, single_region_mem_at}; - use crate::vmm_config::balloon::{BalloonBuilder, BalloonDeviceConfig, BALLOON_DEV_ID}; + use crate::utils::mib_to_bytes; + use crate::vmm_config::balloon::{BALLOON_DEV_ID, BalloonBuilder, BalloonDeviceConfig}; use crate::vmm_config::boot_source::DEFAULT_KERNEL_CMDLINE; use crate::vmm_config::drive::{BlockBuilder, BlockDeviceConfig}; use crate::vmm_config::entropy::{EntropyDeviceBuilder, EntropyDeviceConfig}; use crate::vmm_config::net::{NetBuilder, NetworkInterfaceConfig}; use crate::vmm_config::vsock::tests::default_config; use crate::vmm_config::vsock::{VsockBuilder, VsockDeviceConfig}; + use crate::vstate::vm::tests::setup_vm_with_memory; #[derive(Debug)] pub(crate) struct CustomBlockConfig { @@ -1099,15 +816,8 @@ pub mod tests { } pub(crate) fn default_vmm() -> Vmm { - let guest_memory = arch_mem(128 << 20); - - let vcpus_exit_evt = EventFd::new(libc::EFD_NONBLOCK) - .map_err(VmmError::EventFd) - .map_err(StartMicrovmError::Internal) - .unwrap(); + let (kvm, mut vm) = setup_vm_with_memory(mib_to_bytes(128)); - let mut vm = Vm::new(vec![]).unwrap(); - vm.memory_init(&guest_memory, false).unwrap(); let mmio_device_manager = MMIODeviceManager::new(); let acpi_device_manager = ACPIDeviceManager::new(); #[cfg(target_arch = "x86_64")] @@ -1126,22 +836,14 @@ pub mod tests { ) .unwrap(); - #[cfg(target_arch = "x86_64")] - setup_interrupt_controller(&mut vm).unwrap(); - - #[cfg(target_arch = "aarch64")] - { - let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let _vcpu = Vcpu::new(1, &vm, exit_evt).unwrap(); - setup_interrupt_controller(&mut vm, 1).unwrap(); - } + let (_, vcpus_exit_evt) = vm.create_vcpus(1).unwrap(); Vmm { events_observer: Some(std::io::stdin()), instance_info: InstanceInfo::default(), shutdown_exit_code: None, + kvm, vm, - guest_memory, uffd: None, vcpus_handles: Vec::new(), vcpus_exit_evt, @@ -1244,10 +946,11 @@ pub mod tests { attach_unixsock_vsock_device(vmm, cmdline, &vsock, event_manager).unwrap(); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_VSOCK), &vsock_dev_id) - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_VSOCK), &vsock_dev_id) + .is_some() + ); } pub(crate) fn insert_entropy_device( @@ -1261,10 +964,11 @@ pub mod tests { attach_entropy_device(vmm, cmdline, &entropy, event_manager).unwrap(); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_RNG), ENTROPY_DEV_ID) - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_RNG), ENTROPY_DEV_ID) + .is_some() + ); } #[cfg(target_arch = "x86_64")] @@ -1285,90 +989,13 @@ pub mod tests { attach_balloon_device(vmm, cmdline, balloon, event_manager).unwrap(); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BALLOON), BALLOON_DEV_ID) - .is_some()); - } - - fn make_test_bin() -> Vec { - let mut fake_bin = Vec::new(); - fake_bin.resize(1_000_000, 0xAA); - fake_bin - } - - #[test] - // Test that loading the initrd is successful on different archs. - fn test_load_initrd() { - use crate::vstate::memory::GuestMemory; - let image = make_test_bin(); - - let mem_size: usize = image.len() * 2 + crate::arch::PAGE_SIZE; - - let tempfile = TempFile::new().unwrap(); - let mut tempfile = tempfile.into_file(); - tempfile.write_all(&image).unwrap(); - - #[cfg(target_arch = "x86_64")] - let gm = single_region_mem(mem_size); - - #[cfg(target_arch = "aarch64")] - let gm = single_region_mem(mem_size + crate::arch::aarch64::layout::FDT_MAX_SIZE); - - let res = load_initrd(&gm, &mut tempfile); - let initrd = res.unwrap(); - assert!(gm.address_in_range(initrd.address)); - assert_eq!(initrd.size, image.len()); - } - - #[test] - fn test_load_initrd_no_memory() { - let gm = single_region_mem(79); - let image = make_test_bin(); - let tempfile = TempFile::new().unwrap(); - let mut tempfile = tempfile.into_file(); - tempfile.write_all(&image).unwrap(); - let res = load_initrd(&gm, &mut tempfile); - assert!( - matches!(res, Err(StartMicrovmError::InitrdLoad)), - "{:?}", - res - ); - } - - #[test] - fn test_load_initrd_unaligned() { - let image = vec![1, 2, 3, 4]; - let tempfile = TempFile::new().unwrap(); - let mut tempfile = tempfile.into_file(); - tempfile.write_all(&image).unwrap(); - let gm = single_region_mem_at(crate::arch::PAGE_SIZE as u64 + 1, image.len() * 2); - - let res = load_initrd(&gm, &mut tempfile); assert!( - matches!(res, Err(StartMicrovmError::InitrdLoad)), - "{:?}", - res + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BALLOON), BALLOON_DEV_ID) + .is_some() ); } - #[test] - fn test_create_vcpus() { - let vcpu_count = 2; - let guest_memory = arch_mem(128 << 20); - - #[allow(unused_mut)] - let mut vm = Vm::new(vec![]).unwrap(); - vm.memory_init(&guest_memory, false).unwrap(); - let evfd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - - #[cfg(target_arch = "x86_64")] - setup_interrupt_controller(&mut vm).unwrap(); - - let vcpu_vec = create_vcpus(&vm, vcpu_count, &evfd).unwrap(); - assert_eq!(vcpu_vec.len(), vcpu_count as usize); - } - #[test] fn test_attach_net_devices() { let mut event_manager = EventManager::new().expect("Unable to create EventManager"); @@ -1413,10 +1040,11 @@ pub mod tests { let mut cmdline = default_kernel_cmdline(); insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=/dev/vda ro")); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) + .is_some() + ); } // Use case 2: root block device is specified through PARTUUID. @@ -1433,10 +1061,11 @@ pub mod tests { let mut cmdline = default_kernel_cmdline(); insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=PARTUUID=0eaa91a0-01 rw")); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) + .is_some() + ); } // Use case 3: root block device is not added at all. @@ -1454,10 +1083,11 @@ pub mod tests { insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(!cmdline_contains(&cmdline, "root=PARTUUID=")); assert!(!cmdline_contains(&cmdline, "root=/dev/vda")); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) + .is_some() + ); } // Use case 4: rw root block device and other rw and ro drives. @@ -1490,18 +1120,21 @@ pub mod tests { insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=PARTUUID=0eaa91a0-01 rw")); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BLOCK), "root") - .is_some()); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BLOCK), "secondary") - .is_some()); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BLOCK), "third") - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BLOCK), "root") + .is_some() + ); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BLOCK), "secondary") + .is_some() + ); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BLOCK), "third") + .is_some() + ); // Check if these three block devices are inserted in kernel_cmdline. #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -1526,10 +1159,11 @@ pub mod tests { let mut cmdline = default_kernel_cmdline(); insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=/dev/vda rw")); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) + .is_some() + ); } // Use case 6: root block device is ro, with PARTUUID. @@ -1546,10 +1180,11 @@ pub mod tests { let mut cmdline = default_kernel_cmdline(); insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=PARTUUID=0eaa91a0-01 ro")); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) + .is_some() + ); } // Use case 7: root block device is rw with flush enabled @@ -1566,10 +1201,11 @@ pub mod tests { let mut cmdline = default_kernel_cmdline(); insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs); assert!(cmdline_contains(&cmdline, "root=/dev/vda rw")); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::Virtio(TYPE_BLOCK), drive_id.as_str()) + .is_some() + ); } } @@ -1580,10 +1216,11 @@ pub mod tests { let res = attach_boot_timer_device(&mut vmm, request_ts); res.unwrap(); - assert!(vmm - .mmio_device_manager - .get_device(DeviceType::BootTimer, &DeviceType::BootTimer.to_string()) - .is_some()); + assert!( + vmm.mmio_device_manager + .get_device(DeviceType::BootTimer, &DeviceType::BootTimer.to_string()) + .is_some() + ); } #[test] diff --git a/src/vmm/src/cpu_config/aarch64/custom_cpu_template.rs b/src/vmm/src/cpu_config/aarch64/custom_cpu_template.rs index 8c51916bafc..2869f28ff4a 100644 --- a/src/vmm/src/cpu_config/aarch64/custom_cpu_template.rs +++ b/src/vmm/src/cpu_config/aarch64/custom_cpu_template.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use serde::de::Error; use serde::{Deserialize, Serialize}; -use crate::arch::aarch64::regs::{reg_size, RegSize}; +use crate::arch::aarch64::regs::{RegSize, reg_size}; use crate::cpu_config::aarch64::static_cpu_templates::v1n1; use crate::cpu_config::templates::{ CpuTemplateType, GetCpuTemplate, GetCpuTemplateError, KvmCapability, RegisterValueFilter, @@ -79,7 +79,7 @@ impl CustomCpuTemplate { "Invalid aarch64 register address: {:#x} - Only 32, 64 and 128 bit wide \ registers are supported", modifier.addr - ))) + ))); } } } @@ -116,7 +116,7 @@ mod tests { use serde_json::Value; use super::*; - use crate::cpu_config::templates::test_utils::{build_test_template, TEST_TEMPLATE_JSON}; + use crate::cpu_config::templates::test_utils::{TEST_TEMPLATE_JSON, build_test_template}; #[test] fn test_get_cpu_template_with_no_template() { @@ -231,10 +231,12 @@ mod tests { ] }"#, ); - assert!(cpu_config_result - .unwrap_err() - .to_string() - .contains("Failed to parse string [0bK] as a number for CPU template")); + assert!( + cpu_config_result + .unwrap_err() + .to_string() + .contains("Failed to parse string [0bK] as a number for CPU template") + ); // Malformed 64-bit bitmap - filter failed let cpu_config_result = serde_json::from_str::( @@ -262,10 +264,11 @@ mod tests { ] }"#, ); - assert!(cpu_config_result - .unwrap_err() - .to_string() - .contains("Failed to parse string [0bx00100x0x1xxxx05xxx1xxxxxxxxxxx1] as a bitmap")); + assert!( + cpu_config_result.unwrap_err().to_string().contains( + "Failed to parse string [0bx00100x0x1xxxx05xxx1xxxxxxxxxxx1] as a bitmap" + ) + ); } #[test] diff --git a/src/vmm/src/cpu_config/aarch64/mod.rs b/src/vmm/src/cpu_config/aarch64/mod.rs index d11a4f8c0ab..786183f3988 100644 --- a/src/vmm/src/cpu_config/aarch64/mod.rs +++ b/src/vmm/src/cpu_config/aarch64/mod.rs @@ -9,13 +9,19 @@ pub mod static_cpu_templates; pub mod test_utils; use super::templates::CustomCpuTemplate; +use crate::Vcpu; use crate::arch::aarch64::regs::{Aarch64RegisterVec, RegSize}; -use crate::arch::aarch64::vcpu::VcpuError as ArchError; +use crate::arch::aarch64::vcpu::{VcpuArchError, get_registers}; +use crate::vstate::vcpu::KvmVcpuError; /// Errors thrown while configuring templates. -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[error("Failed to create a guest cpu configuration: {0}")] -pub struct CpuConfigurationError(#[from] pub ArchError); +#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] +pub enum CpuConfigurationError { + /// Error initializing the vcpu: {0} + VcpuInit(#[from] KvmVcpuError), + /// Error reading vcpu registers: {0} + VcpuGetRegs(#[from] VcpuArchError), +} /// CPU configuration for aarch64 #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -25,11 +31,22 @@ pub struct CpuConfiguration { } impl CpuConfiguration { - /// Creates new guest CPU config based on the provided template - pub fn apply_template( - mut self, - template: &CustomCpuTemplate, + /// Create new CpuConfiguration. + pub fn new( + cpu_template: &CustomCpuTemplate, + vcpus: &mut [Vcpu], ) -> Result { + for vcpu in vcpus.iter_mut() { + vcpu.kvm_vcpu.init(&cpu_template.vcpu_features)?; + } + + let mut regs = Aarch64RegisterVec::default(); + get_registers(&vcpus[0].kvm_vcpu.fd, &cpu_template.reg_list(), &mut regs)?; + Ok(CpuConfiguration { regs }) + } + + /// Creates new guest CPU config based on the provided template + pub fn apply_template(mut self, template: &CustomCpuTemplate) -> Self { for (modifier, mut reg) in template.reg_modifiers.iter().zip(self.regs.iter_mut()) { match reg.size() { RegSize::U32 => { @@ -50,7 +67,7 @@ impl CpuConfiguration { _ => unreachable!("Only 32, 64 and 128 bit wide registers are supported"), } } - Ok(self) + self } /// Returns ids of registers that are changed diff --git a/src/vmm/src/cpu_config/mod.rs b/src/vmm/src/cpu_config/mod.rs index 3e438d07ef4..4c7404a14d3 100644 --- a/src/vmm/src/cpu_config/mod.rs +++ b/src/vmm/src/cpu_config/mod.rs @@ -15,4 +15,4 @@ pub mod x86_64; pub mod aarch64; #[cfg(test)] -pub mod test_utils; +pub(crate) mod test_utils; diff --git a/src/vmm/src/cpu_config/templates.rs b/src/vmm/src/cpu_config/templates.rs index 984fd8dcac2..559da632cc4 100644 --- a/src/vmm/src/cpu_config/templates.rs +++ b/src/vmm/src/cpu_config/templates.rs @@ -6,7 +6,7 @@ mod common_types { pub use crate::cpu_config::x86_64::custom_cpu_template::CustomCpuTemplate; pub use crate::cpu_config::x86_64::static_cpu_templates::StaticCpuTemplate; pub use crate::cpu_config::x86_64::{ - test_utils, CpuConfiguration, CpuConfigurationError as GuestConfigError, + CpuConfiguration, CpuConfigurationError as GuestConfigError, test_utils, }; } @@ -15,7 +15,7 @@ mod common_types { pub use crate::cpu_config::aarch64::custom_cpu_template::CustomCpuTemplate; pub use crate::cpu_config::aarch64::static_cpu_templates::StaticCpuTemplate; pub use crate::cpu_config::aarch64::{ - test_utils, CpuConfiguration, CpuConfigurationError as GuestConfigError, + CpuConfiguration, CpuConfigurationError as GuestConfigError, test_utils, }; } @@ -83,7 +83,7 @@ impl From<&CpuTemplateType> for StaticCpuTemplate { } } -impl<'a> TryFrom<&'a [u8]> for CustomCpuTemplate { +impl TryFrom<&[u8]> for CustomCpuTemplate { type Error = serde_json::Error; fn try_from(value: &[u8]) -> Result { @@ -250,7 +250,7 @@ where return Err(D::Error::custom(format!( "Failed to parse string [{}] as a bitmap - unknown character: {}", original_str, c - ))) + ))); } } i += 1; diff --git a/src/vmm/src/cpu_config/templates_serde.rs b/src/vmm/src/cpu_config/templates_serde.rs index 11eb6062853..91bb37b29dc 100644 --- a/src/vmm/src/cpu_config/templates_serde.rs +++ b/src/vmm/src/cpu_config/templates_serde.rs @@ -51,8 +51,8 @@ deserialize_from_str!(deserialize_from_str_u64, u64); #[cfg(test)] mod tests { - use serde::de::value::{Error, StrDeserializer}; use serde::de::IntoDeserializer; + use serde::de::value::{Error, StrDeserializer}; use super::*; diff --git a/src/vmm/src/cpu_config/test_utils.rs b/src/vmm/src/cpu_config/test_utils.rs index de2e952522e..197c0c62cf6 100644 --- a/src/vmm/src/cpu_config/test_utils.rs +++ b/src/vmm/src/cpu_config/test_utils.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use crate::cpu_config::templates::CustomCpuTemplate; -// Get a static CPU template stored as a JSON file. +/// Get a static CPU template stored as a JSON file. pub fn get_json_template(filename: &str) -> CustomCpuTemplate { let json_path = [ env!("CARGO_MANIFEST_DIR"), diff --git a/src/vmm/src/cpu_config/x86_64/cpuid/amd/normalize.rs b/src/vmm/src/cpu_config/x86_64/cpuid/amd/normalize.rs index 5a682441e8d..e481bc17681 100644 --- a/src/vmm/src/cpu_config/x86_64/cpuid/amd/normalize.rs +++ b/src/vmm/src/cpu_config/x86_64/cpuid/amd/normalize.rs @@ -1,13 +1,13 @@ // Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use crate::cpu_config::x86_64::cpuid::common::{get_vendor_id_from_host, GetCpuidError}; +use crate::cpu_config::x86_64::cpuid::common::{GetCpuidError, get_vendor_id_from_host}; use crate::cpu_config::x86_64::cpuid::normalize::{ - get_range, set_bit, set_range, CheckedAssignError, + CheckedAssignError, get_range, set_bit, set_range, }; use crate::cpu_config::x86_64::cpuid::{ - cpuid, cpuid_count, CpuidEntry, CpuidKey, CpuidRegisters, CpuidTrait, KvmCpuidFlags, - MissingBrandStringLeaves, BRAND_STRING_LENGTH, VENDOR_ID_AMD, + BRAND_STRING_LENGTH, CpuidEntry, CpuidKey, CpuidRegisters, CpuidTrait, KvmCpuidFlags, + MissingBrandStringLeaves, VENDOR_ID_AMD, cpuid, cpuid_count, }; /// Error type for [`super::AmdCpuid::normalize`]. @@ -48,10 +48,10 @@ pub enum PassthroughCacheTopologyError { pub enum FeatureEntryError { /// Missing leaf 0x80000008. MissingLeaf0x80000008, - /// Failed to set `nt` (number of physical threads) due to overflow. - NumberOfPhysicalThreadsOverflow, - /// Failed to set `nt` (number of physical threads). + /// Failed to set number of physical threads (CPUID.80000008H:ECX[7:0]): {0} NumberOfPhysicalThreads(CheckedAssignError), + /// Failed to set number of physical threads (CPUID.80000008H:ECX[7:0]) due to overflow. + NumberOfPhysicalThreadsOverflow, } /// Error type for setting leaf 0x8000001d section of [`super::AmdCpuid::normalize`]. @@ -59,22 +59,24 @@ pub enum FeatureEntryError { pub enum ExtendedCacheTopologyError { /// Missing leaf 0x8000001d. MissingLeaf0x8000001d, - /// Failed to set `num_sharing_cache` due to overflow. - NumSharingCacheOverflow, - /// Failed to set `num_sharing_cache`: {0} - NumSharingCache(CheckedAssignError), + #[rustfmt::skip] + /// Failed to set number of logical processors sharing cache(CPUID.(EAX=8000001DH,ECX={0}):EAX[25:14]): {1} + NumSharingCache(u32, CheckedAssignError), + #[rustfmt::skip] + /// Failed to set number of logical processors sharing cache (CPUID.(EAX=8000001DH,ECX={0}):EAX[25:14]) due to overflow. + NumSharingCacheOverflow(u32), } /// Error type for setting leaf 0x8000001e section of [`super::AmdCpuid::normalize`]. #[derive(Debug, thiserror::Error, displaydoc::Display, Eq, PartialEq)] pub enum ExtendedApicIdError { + /// Failed to set compute unit ID (CPUID.8000001EH:EBX[7:0]): {0} + ComputeUnitId(CheckedAssignError), + /// Failed to set extended APIC ID (CPUID.8000001EH:EAX[31:0]): {0} + ExtendedApicId(CheckedAssignError), /// Missing leaf 0x8000001e. MissingLeaf0x8000001e, - /// Failed to set `extended_apic_id`: {0} - ExtendedApicId(CheckedAssignError), - /// Failed to set `compute_unit_id`: {0} - ComputeUnitId(CheckedAssignError), - /// Failed to set `threads_per_compute_unit`: {0} + /// Failed to set threads per core unit (CPUID:8000001EH:EBX[15:8]): {0} ThreadPerComputeUnit(CheckedAssignError), } @@ -104,7 +106,6 @@ impl super::AmdCpuid { ) -> Result<(), NormalizeCpuidError> { self.passthrough_cache_topology()?; self.update_structured_extended_entry()?; - self.update_largest_extended_fn_entry()?; self.update_extended_feature_fn_entry()?; self.update_amd_feature_entry(cpu_count)?; self.update_extended_cache_topology_entry(cpu_count, cpus_per_core)?; @@ -154,6 +155,7 @@ impl super::AmdCpuid { // On non-AMD hosts this condition may never be true thus this loop may be // indefinite. + // CPUID Fn8000_0001D_EAX_x[4:0] (Field Name: CacheType) // Cache type. Identifies the type of cache. // ```text // Bits Description @@ -163,8 +165,6 @@ impl super::AmdCpuid { // 03h Unified cache // 1Fh-04h Reserved. // ``` - // - // cache_type: 0..4, let cache_type = result.eax & 15; if cache_type == 0 { break; @@ -181,33 +181,15 @@ impl super::AmdCpuid { Ok(()) } - /// Update largest extended fn entry. - #[allow(clippy::unwrap_used, clippy::unwrap_in_result)] - fn update_largest_extended_fn_entry(&mut self) -> Result<(), NormalizeCpuidError> { - // KVM sets the largest extended function to 0x80000000. Change it to 0x8000001f - // Since we also use the leaf 0x8000001d (Extended Cache Topology). - let leaf_80000000 = self - .get_mut(&CpuidKey::leaf(0x80000000)) - .ok_or(NormalizeCpuidError::MissingLeaf0x80000000)?; - - // Largest extended function. The largest CPUID extended function input value supported by - // the processor implementation. - // - // l_func_ext: 0..32, - set_range(&mut leaf_80000000.result.eax, 0..32, 0x8000_001f).unwrap(); - Ok(()) - } - /// Updated extended feature fn entry. fn update_extended_feature_fn_entry(&mut self) -> Result<(), NormalizeCpuidError> { // set the Topology Extension bit since we use the Extended Cache Topology leaf let leaf_80000001 = self .get_mut(&CpuidKey::leaf(0x80000001)) .ok_or(NormalizeCpuidError::MissingLeaf0x80000001)?; + // CPUID Fn8000_0001_ECX[22] (Field Name: TopologyExtensions) // Topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID // Fn8000_001E_EDX. - // - // topology_extensions: 22, set_bit(&mut leaf_80000001.result.ecx, 22, true); Ok(()) } @@ -238,6 +220,7 @@ impl super::AmdCpuid { .get_mut(&CpuidKey::leaf(0x80000008)) .ok_or(FeatureEntryError::MissingLeaf0x80000008)?; + // CPUID Fn8000_0008_ECX[15:12] (Field Name: ApicIdSize) // APIC ID size. The number of bits in the initial APIC20[ApicId] value that indicate // logical processor ID within a package. The size of this field determines the // maximum number of logical processors (MNLP) that the package could @@ -246,19 +229,15 @@ impl super::AmdCpuid { // Fn8000_0008_ECX[NC]. A value of zero indicates that legacy methods must be // used to determine the maximum number of logical processors, as indicated by // CPUID Fn8000_0008_ECX[NC]. - // - // apic_id_size: 12..16, - set_range(&mut leaf_80000008.result.ecx, 12..16, THREAD_ID_MAX_SIZE).unwrap(); + set_range(&mut leaf_80000008.result.ecx, 12..=15, THREAD_ID_MAX_SIZE).unwrap(); + // CPUID Fn8000_0008_ECX[7:0] (Field Name: NC) // Number of physical threads - 1. The number of threads in the processor is NT+1 // (e.g., if NT = 0, then there is one thread). See “Legacy Method” on page 633. - // - // nt: 0..8, - // let sub = cpu_count .checked_sub(1) .ok_or(FeatureEntryError::NumberOfPhysicalThreadsOverflow)?; - set_range(&mut leaf_80000008.result.ecx, 0..8, u32::from(sub)) + set_range(&mut leaf_80000008.result.ecx, 0..=7, u32::from(sub)) .map_err(FeatureEntryError::NumberOfPhysicalThreads)?; Ok(()) @@ -273,6 +252,7 @@ impl super::AmdCpuid { ) -> Result<(), ExtendedCacheTopologyError> { for i in 0.. { if let Some(subleaf) = self.get_mut(&CpuidKey::subleaf(0x8000001d, i)) { + // CPUID Fn8000_001D_EAX_x[7:5] (Field Name: CacheLevel) // Cache level. Identifies the level of this cache. Note that the enumeration value // is not necessarily equal to the cache level. // ```text @@ -283,10 +263,9 @@ impl super::AmdCpuid { // 011b Level 3 // 111b-100b Reserved. // ``` - // - // cache_level: 5..8 - let cache_level = get_range(subleaf.result.eax, 5..8); + let cache_level = get_range(subleaf.result.eax, 5..=7); + // CPUID Fn8000_001D_EAX_x[25:14] (Field Name: NumSharingCache) // Specifies the number of logical processors sharing the cache enumerated by N, // the value passed to the instruction in ECX. The number of logical processors // sharing this cache is the value of this field incremented by 1. To determine @@ -297,8 +276,6 @@ impl super::AmdCpuid { // // Logical processors with the same ShareId then share a cache. If // NumSharingCache+1 is not a power of two, round it up to the next power of two. - // - // num_sharing_cache: 14..26, match cache_level { // L1 & L2 Cache @@ -306,17 +283,17 @@ impl super::AmdCpuid { 1 | 2 => { // SAFETY: We know `cpus_per_core > 0` therefore this is always safe. let sub = u32::from(cpus_per_core.checked_sub(1).unwrap()); - set_range(&mut subleaf.result.eax, 14..26, sub) - .map_err(ExtendedCacheTopologyError::NumSharingCache)?; + set_range(&mut subleaf.result.eax, 14..=25, sub) + .map_err(|err| ExtendedCacheTopologyError::NumSharingCache(i, err))?; } // L3 Cache // The L3 cache is shared among all the logical threads 3 => { let sub = cpu_count .checked_sub(1) - .ok_or(ExtendedCacheTopologyError::NumSharingCacheOverflow)?; - set_range(&mut subleaf.result.eax, 14..26, u32::from(sub)) - .map_err(ExtendedCacheTopologyError::NumSharingCache)?; + .ok_or(ExtendedCacheTopologyError::NumSharingCacheOverflow(i))?; + set_range(&mut subleaf.result.eax, 14..=25, u32::from(sub)) + .map_err(|err| ExtendedCacheTopologyError::NumSharingCache(i, err))?; } _ => (), } @@ -352,16 +329,18 @@ impl super::AmdCpuid { .get_mut(&CpuidKey::leaf(0x8000001e)) .ok_or(ExtendedApicIdError::MissingLeaf0x8000001e)?; + // CPUID Fn8000_001E_EAX[31:0] (Field Name: ExtendedApicId) // Extended APIC ID. If MSR0000_001B[ApicEn] = 0, this field is reserved. - // - // extended_apic_id: 0..32, - set_range(&mut leaf_8000001e.result.eax, 0..32, u32::from(cpu_index)) + set_range(&mut leaf_8000001e.result.eax, 0..=31, u32::from(cpu_index)) .map_err(ExtendedApicIdError::ExtendedApicId)?; - // compute_unit_id: 0..8, - set_range(&mut leaf_8000001e.result.ebx, 0..8, core_id) + // CPUID Fn8000_001E_EBX[7:0] (Field Name: ComputeUnitId) + // Compute unit ID. Identifies a Compute Unit, which may be one or more physical cores that + // each implement one or more logical processors. + set_range(&mut leaf_8000001e.result.ebx, 0..=7, core_id) .map_err(ExtendedApicIdError::ComputeUnitId)?; + // CPUID Fn8000_001E_EBX[15:8] (Field Name: ThreadsPerComputeUnit) // Threads per compute unit (zero-based count). The actual number of threads // per compute unit is the value of this field + 1. To determine which logical // processors (threads) belong to a given Compute Unit, determine a ShareId @@ -373,30 +352,26 @@ impl super::AmdCpuid { // Unit. (If ThreadsPerComputeUnit+1 is not a power of two, round it up to the // next power of two). // - // threads_per_compute_unit: 8..16, - // // SAFETY: We know `cpus_per_core > 0` therefore this is always safe. let sub = u32::from(cpus_per_core.checked_sub(1).unwrap()); - set_range(&mut leaf_8000001e.result.ebx, 8..16, sub) + set_range(&mut leaf_8000001e.result.ebx, 8..=15, sub) .map_err(ExtendedApicIdError::ThreadPerComputeUnit)?; + // CPUID Fn8000_001E_ECX[10:8] (Field Name: NodesPerProcessor) // Specifies the number of nodes in the package/socket in which this logical // processor resides. Node in this context corresponds to a processor die. // Encoding is N-1, where N is the number of nodes present in the socket. // - // nodes_per_processor: 8..11, - // // SAFETY: We know the value always fits within the range and thus is always safe. // Set nodes per processor. - set_range(&mut leaf_8000001e.result.ecx, 8..11, NODES_PER_PROCESSOR).unwrap(); + set_range(&mut leaf_8000001e.result.ecx, 8..=10, NODES_PER_PROCESSOR).unwrap(); + // CPUID Fn8000_001E_ECX[7:0] (Field Name: NodeId) // Specifies the ID of the node containing the current logical processor. NodeId // values are unique across the system. // - // node_id: 0..8, - // // Put all the cpus in the same node. - set_range(&mut leaf_8000001e.result.ecx, 0..8, 0).unwrap(); + set_range(&mut leaf_8000001e.result.ecx, 0..=7, 0).unwrap(); Ok(()) } diff --git a/src/vmm/src/cpu_config/x86_64/cpuid/common.rs b/src/vmm/src/cpu_config/x86_64/cpuid/common.rs index 3b89183293c..4046346e125 100644 --- a/src/vmm/src/cpu_config/x86_64/cpuid/common.rs +++ b/src/vmm/src/cpu_config/x86_64/cpuid/common.rs @@ -98,7 +98,7 @@ pub(crate) fn msrs_to_save_by_cpuid(cpuid: &kvm_bindings::CpuId) -> Vec { 0, ebx, MPX_BITINDEX, - [crate::arch::x86_64::gen::msr_index::MSR_IA32_BNDCFGS] + [crate::arch::x86_64::generated::msr_index::MSR_IA32_BNDCFGS] ); // IA32_MTRR_PHYSBASEn, IA32_MTRR_PHYSMASKn diff --git a/src/vmm/src/cpu_config/x86_64/cpuid/intel/normalize.rs b/src/vmm/src/cpu_config/x86_64/cpuid/intel/normalize.rs index 74536e44241..fa5e6766635 100644 --- a/src/vmm/src/cpu_config/x86_64/cpuid/intel/normalize.rs +++ b/src/vmm/src/cpu_config/x86_64/cpuid/intel/normalize.rs @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use crate::cpu_config::x86_64::cpuid::normalize::{ - get_range, set_bit, set_range, CheckedAssignError, + CheckedAssignError, get_range, set_bit, set_range, }; use crate::cpu_config::x86_64::cpuid::{ - host_brand_string, CpuidKey, CpuidRegisters, CpuidTrait, MissingBrandStringLeaves, - BRAND_STRING_LENGTH, + BRAND_STRING_LENGTH, CpuidKey, CpuidRegisters, CpuidTrait, MissingBrandStringLeaves, + host_brand_string, }; /// Error type for [`super::IntelCpuid::normalize`]. @@ -34,14 +34,14 @@ pub enum NormalizeCpuidError { #[allow(clippy::enum_variant_names)] #[derive(Debug, thiserror::Error, displaydoc::Display, Eq, PartialEq)] pub enum DeterministicCacheError { - /// Failed to set `Maximum number of addressable IDs for logical processors sharing this cache` due to underflow in cpu count. - MaxCpusPerCoreUnderflow, - /// Failed to set `Maximum number of addressable IDs for logical processors sharing this cache`: {0}. - MaxCpusPerCore(CheckedAssignError), - /// Failed to set `Maximum number of addressable IDs for processor cores in the physical package` due to underflow in cores. - MaxCorePerPackageUnderflow, - /// Failed to set `Maximum number of addressable IDs for processor cores in the physical package`: {0}. + /// Failed to set max addressable core ID in physical package (CPUID.04H:EAX[31:26]): {0}. MaxCorePerPackage(CheckedAssignError), + /// Failed to set max addressable core ID in physical package (CPUID.04H:EAX[31:26]) due to underflow in cores. + MaxCorePerPackageUnderflow, + /// Failed to set max addressable processor ID sharing cache (CPUID.04H:EAX[25:14]): {0}. + MaxCpusPerCore(CheckedAssignError), + /// Failed to set max addressable processor ID sharing cache (CPUID.04H:EAX[25:14]) due to underflow in cpu count. + MaxCpusPerCoreUnderflow, } /// We always use this brand string. @@ -73,6 +73,7 @@ impl super::IntelCpuid { self.update_power_management_entry()?; self.update_extended_feature_flags_entry()?; self.update_performance_monitoring_entry()?; + self.update_extended_topology_v2_entry(); self.update_brand_string_entry()?; Ok(()) @@ -97,18 +98,16 @@ impl super::IntelCpuid { break; } + // CPUID.04H:EAX[7:5] // Cache Level (Starts at 1) - // - // cache_level: 5..8 - let cache_level = get_range(subleaf.result.eax, 5..8); + let cache_level = get_range(subleaf.result.eax, 5..=7); + // CPUID.04H:EAX[25:14] // Maximum number of addressable IDs for logical processors sharing this cache. // - Add one to the return value to get the result. // - The nearest power-of-2 integer that is not smaller than (1 + EAX[25:14]) is the // number of unique initial APIC IDs reserved for addressing different logical // processors sharing this cache. - // - // max_num_addressable_ids_for_logical_processors_sharing_this_cache: 14..26, // We know `cpus_per_core > 0` therefore `cpus_per_core.checked_sub(1).unwrap()` is // always safe. @@ -118,7 +117,7 @@ impl super::IntelCpuid { // The L1 & L2 cache is shared by at most 2 hyperthreads 1 | 2 => { let sub = u32::from(cpus_per_core.checked_sub(1).unwrap()); - set_range(&mut subleaf.result.eax, 14..26, sub) + set_range(&mut subleaf.result.eax, 14..=25, sub) .map_err(DeterministicCacheError::MaxCpusPerCore)?; } // L3 Cache @@ -129,7 +128,7 @@ impl super::IntelCpuid { .checked_sub(1) .ok_or(DeterministicCacheError::MaxCpusPerCoreUnderflow)?, ); - set_range(&mut subleaf.result.eax, 14..26, sub) + set_range(&mut subleaf.result.eax, 14..=25, sub) .map_err(DeterministicCacheError::MaxCpusPerCore)?; } _ => (), @@ -139,6 +138,7 @@ impl super::IntelCpuid { #[allow(clippy::unwrap_used)] let cores = cpu_count.checked_div(cpus_per_core).unwrap(); + // CPUID.04H:EAX[31:26] // Maximum number of addressable IDs for processor cores in the physical package. // - Add one to the return value to get the result. // - The nearest power-of-2 integer that is not smaller than (1 + EAX[31:26]) is the @@ -146,14 +146,12 @@ impl super::IntelCpuid { // a physical package. Core ID is a subset of bits of the initial APIC ID. // - The returned value is constant for valid initial values in ECX. Valid ECX // values start from 0. - // - // max_num_addressable_ids_for_processor_cores_in_physical_package: 26..32, // Put all the cores in the same socket let sub = u32::from(cores) .checked_sub(1) .ok_or(DeterministicCacheError::MaxCorePerPackageUnderflow)?; - set_range(&mut subleaf.result.eax, 26..32, sub) + set_range(&mut subleaf.result.eax, 26..=31, sub) .map_err(DeterministicCacheError::MaxCorePerPackage)?; } else { break; @@ -168,16 +166,14 @@ impl super::IntelCpuid { .get_mut(&CpuidKey::leaf(0x6)) .ok_or(NormalizeCpuidError::MissingLeaf6)?; + // CPUID.06H:EAX[1] // Intel Turbo Boost Technology available (see description of IA32_MISC_ENABLE[38]). - // - // intel_turbo_boost_technology: 1, set_bit(&mut leaf_6.result.eax, 1, false); + // CPUID.06H:ECX[3] // The processor supports performance-energy bias preference if CPUID.06H:ECX.SETBH[bit 3] // is set and it also implies the presence of a new architectural MSR called // IA32_ENERGY_PERF_BIAS (1B0H). - // - // performance_energy_bias: 3, // Clear X86 EPB feature. No frequency selection in the hypervisor. set_bit(&mut leaf_6.result.ecx, 3, false); @@ -190,13 +186,56 @@ impl super::IntelCpuid { .get_mut(&CpuidKey::subleaf(0x7, 0)) .ok_or(NormalizeCpuidError::MissingLeaf7)?; - // Set FDP_EXCPTN_ONLY bit (bit 6) and ZERO_FCS_FDS bit (bit 13) as recommended in kernel - // doc. These bits are reserved in AMD. + // Set the following bits as recommended in kernel doc. These bits are reserved in AMD. + // - CPUID.07H:EBX[6] (FDP_EXCPTN_ONLY) + // - CPUID.07H:EBX[13] (Deprecates FPU CS and FPU DS values) // https://lore.kernel.org/all/20220322110712.222449-3-pbonzini@redhat.com/ // https://github.com/torvalds/linux/commit/45016721de3c714902c6f475b705e10ae0bdd801 set_bit(&mut leaf_7_0.result.ebx, 6, true); set_bit(&mut leaf_7_0.result.ebx, 13, true); + // CPUID.(EAX=07H,ECX=0):ECX[5] (Mnemonic: WAITPKG) + // + // WAITPKG indicates support of user wait instructions (UMONITOR, UMWAIT and TPAUSE). + // - UMONITOR arms address monitoring hardware that checks for store operations on the + // specified address range. + // - UMWAIT instructs the processor to enter an implementation-dependent optimized state + // (either a light-weight power/performance optimized state (C0.1 idle state) or an + // improved power/performance optimized state (C0.2 idle state)) while monitoring the + // address range specified in UMONITOR. The instruction wakes up when the time-stamp + // counter reaches or exceeds the implicit EDX:EAX 64-bit input value. + // - TPAUSE instructs the processor to enter an implementation-dependent optimized state. + // The instruction wakes up when the time-stamp counter reaches or exceeds the implict + // EDX:EAX 64-bit input value. + // + // These instructions may be executed at any privilege level. Even when UMWAIT/TPAUSE are + // executed within a guest, the *physical* processor enters the requested optimized state. + // See Intel SDM vol.3 for more details of the behavior of these instructions in VMX + // non-root operation. + // + // MONITOR/MWAIT instructions are the privileged variant of UMONITOR/UMWAIT and are + // unconditionally emulated as NOP by KVM. + // https://github.com/torvalds/linux/commit/87c00572ba05aa8c9db118da75c608f47eb10b9e + // + // When UMONITOR/UMWAIT/TPAUSE were initially introduced, KVM clears the WAITPKG CPUID bit + // in KVM_GET_SUPPORTED_CPUID by default, and KVM exposed them to guest only when VMM + // explicitly set the bit via KVM_SET_CPUID2 API. + // https://github.com/torvalds/linux/commit/e69e72faa3a0709dd23df6a4ca060a15e99168a1 + // However, since v5.8, if the processor supports "enable user wait and pause" in Intel VMX, + // KVM_GET_SUPPORTED_CPUID sets the bit to 1 to let VMM know that it is available. So if the + // returned value is passed to KVM_SET_CPUID2 API as it is, guests are able to execute them. + // https://github.com/torvalds/linux/commit/0abcc8f65cc23b65bc8d1614cc64b02b1641ed7c + // + // Similar to MONITOR/MWAIT, we disable the guest's WAITPKG in order to prevent a guest from + // executing those instructions and putting a physical processor to an idle state which may + // lead to an overhead of waking it up when scheduling another guest on it. By clearing the + // WAITPKG bit in KVM_SET_CPUID2 API, KVM does not set the "enable user wait and pause" bit + // (bit 26) of the secondary processor-based VM-execution control, which makes guests get + // #UD when attempting to executing those instructions. + // + // Note that the WAITPKG bit is reserved on AMD. + set_bit(&mut leaf_7_0.result.ecx, 5, false); + Ok(()) } @@ -214,6 +253,29 @@ impl super::IntelCpuid { Ok(()) } + /// Update extended topology v2 entry + /// + /// CPUID leaf 1FH is a preferred superset to leaf 0xB. Intel recommends using leaf 0x1F when + /// available rather than leaf 0xB. + /// + /// Since we don't use any domains than ones supported in leaf 0xB, we just copy contents of + /// leaf 0xB to leaf 0x1F. + fn update_extended_topology_v2_entry(&mut self) { + // Skip if leaf 0x1F does not exist. + if self.get(&CpuidKey::leaf(0x1F)).is_none() { + return; + } + + for index in 0.. { + if let Some(subleaf) = self.get(&CpuidKey::subleaf(0xB, index)) { + self.0 + .insert(CpuidKey::subleaf(0x1F, index), subleaf.clone()); + } else { + break; + } + } + } + fn update_brand_string_entry(&mut self) -> Result<(), NormalizeCpuidError> { // Get host brand string. let host_brand_string: [u8; BRAND_STRING_LENGTH] = host_brand_string(); @@ -335,9 +397,12 @@ mod tests { clippy::as_conversions )] + use std::collections::BTreeMap; use std::ffi::CStr; use super::*; + use crate::cpu_config::x86_64::cpuid::{CpuidEntry, IntelCpuid, KvmCpuidFlags}; + #[test] fn default_brand_string_test() { let brand_string = b"Intel(R) Xeon(R) Platinum 8275CL CPU @ 3.00GHz\0\0"; @@ -375,27 +440,129 @@ mod tests { #[test] fn test_update_extended_feature_flags_entry() { - let mut cpuid = - crate::cpu_config::x86_64::cpuid::IntelCpuid(std::collections::BTreeMap::from([( - crate::cpu_config::x86_64::cpuid::CpuidKey { - leaf: 0x7, - subleaf: 0, - }, - crate::cpu_config::x86_64::cpuid::CpuidEntry { - flags: crate::cpu_config::x86_64::cpuid::KvmCpuidFlags::SIGNIFICANT_INDEX, - ..Default::default() - }, - )])); + let mut cpuid = IntelCpuid(BTreeMap::from([( + CpuidKey { + leaf: 0x7, + subleaf: 0, + }, + CpuidEntry { + flags: KvmCpuidFlags::SIGNIFICANT_INDEX, + ..Default::default() + }, + )])); cpuid.update_extended_feature_flags_entry().unwrap(); let leaf_7_0 = cpuid - .get(&crate::cpu_config::x86_64::cpuid::CpuidKey { + .get(&CpuidKey { leaf: 0x7, subleaf: 0, }) .unwrap(); assert!((leaf_7_0.result.ebx & (1 << 6)) > 0); assert!((leaf_7_0.result.ebx & (1 << 13)) > 0); + assert_eq!((leaf_7_0.result.ecx & (1 << 5)), 0); + } + + #[test] + fn test_update_extended_topology_v2_entry_no_leaf_0x1f() { + let mut cpuid = IntelCpuid(BTreeMap::from([( + CpuidKey { + leaf: 0xB, + subleaf: 0, + }, + CpuidEntry { + flags: KvmCpuidFlags::SIGNIFICANT_INDEX, + ..Default::default() + }, + )])); + + cpuid.update_extended_topology_v2_entry(); + + assert!( + cpuid + .get(&CpuidKey { + leaf: 0x1F, + subleaf: 0, + }) + .is_none() + ); + } + + #[test] + fn test_update_extended_topology_v2_entry() { + let mut cpuid = IntelCpuid(BTreeMap::from([ + ( + CpuidKey { + leaf: 0xB, + subleaf: 0, + }, + CpuidEntry { + flags: KvmCpuidFlags::SIGNIFICANT_INDEX, + result: CpuidRegisters { + eax: 0x1, + ebx: 0x2, + ecx: 0x3, + edx: 0x4, + }, + }, + ), + ( + CpuidKey { + leaf: 0xB, + subleaf: 1, + }, + CpuidEntry { + flags: KvmCpuidFlags::SIGNIFICANT_INDEX, + result: CpuidRegisters { + eax: 0xa, + ebx: 0xb, + ecx: 0xc, + edx: 0xd, + }, + }, + ), + ( + CpuidKey { + leaf: 0x1F, + subleaf: 0, + }, + CpuidEntry { + flags: KvmCpuidFlags::SIGNIFICANT_INDEX, + result: CpuidRegisters { + eax: 0xFFFFFFFF, + ebx: 0xFFFFFFFF, + ecx: 0xFFFFFFFF, + edx: 0xFFFFFFFF, + }, + }, + ), + ])); + + cpuid.update_extended_topology_v2_entry(); + + // Check leaf 0x1F, subleaf 0 is updated. + let leaf_1f_0 = cpuid + .get(&CpuidKey { + leaf: 0x1F, + subleaf: 0, + }) + .unwrap(); + assert_eq!(leaf_1f_0.result.eax, 0x1); + assert_eq!(leaf_1f_0.result.ebx, 0x2); + assert_eq!(leaf_1f_0.result.ecx, 0x3); + assert_eq!(leaf_1f_0.result.edx, 0x4); + + // Check lefa 0x1F, subleaf 1 is inserted. + let leaf_1f_1 = cpuid + .get(&CpuidKey { + leaf: 0x1F, + subleaf: 1, + }) + .unwrap(); + assert_eq!(leaf_1f_1.result.eax, 0xa); + assert_eq!(leaf_1f_1.result.ebx, 0xb); + assert_eq!(leaf_1f_1.result.ecx, 0xc); + assert_eq!(leaf_1f_1.result.edx, 0xd); } } diff --git a/src/vmm/src/cpu_config/x86_64/cpuid/mod.rs b/src/vmm/src/cpu_config/x86_64/cpuid/mod.rs index 2350d30479d..41a5409799a 100644 --- a/src/vmm/src/cpu_config/x86_64/cpuid/mod.rs +++ b/src/vmm/src/cpu_config/x86_64/cpuid/mod.rs @@ -720,12 +720,14 @@ mod tests { }), None ); - assert!(cpuid - .get(&CpuidKey { - leaf: 0x0, - subleaf: 0x0, - }) - .is_some()); + assert!( + cpuid + .get(&CpuidKey { + leaf: 0x0, + subleaf: 0x0, + }) + .is_some() + ); } #[test] @@ -738,12 +740,14 @@ mod tests { }), None ); - assert!(cpuid - .get_mut(&CpuidKey { - leaf: 0x0, - subleaf: 0x0, - }) - .is_some()); + assert!( + cpuid + .get_mut(&CpuidKey { + leaf: 0x0, + subleaf: 0x0, + }) + .is_some() + ); } #[test] diff --git a/src/vmm/src/cpu_config/x86_64/cpuid/normalize.rs b/src/vmm/src/cpu_config/x86_64/cpuid/normalize.rs index 2ac00b23a54..f9359036f5f 100644 --- a/src/vmm/src/cpu_config/x86_64/cpuid/normalize.rs +++ b/src/vmm/src/cpu_config/x86_64/cpuid/normalize.rs @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::cpu_config::x86_64::cpuid::{ - cpuid, CpuidEntry, CpuidKey, CpuidRegisters, CpuidTrait, KvmCpuidFlags, + CpuidEntry, CpuidKey, CpuidRegisters, CpuidTrait, KvmCpuidFlags, cpuid, }; +use crate::vmm_config::machine_config::MAX_SUPPORTED_VCPUS; /// Error type for [`super::Cpuid::normalize`]. #[allow(clippy::module_name_repetitions)] @@ -60,16 +61,16 @@ pub enum GetMaxCpusPerPackageError { #[rustfmt::skip] #[derive(Debug, thiserror::Error, displaydoc::Display, Eq, PartialEq)] pub enum ExtendedTopologyError { - /// Failed to set `Number of bits to shift right on x2APIC ID to get a unique topology ID of the next level type`: {0} - ApicId(CheckedAssignError), - /// Failed to set `Number of logical processors at this level type`: {0} - LogicalProcessors(CheckedAssignError), - /// Failed to set `Level Type`: {0} - LevelType(CheckedAssignError), - /// Failed to set `Level Number`: {0} - LevelNumber(CheckedAssignError), - /// Failed to set all leaves, as more than `u32::MAX` sub-leaves are present: {0} - Overflow(>::Error), + /// Failed to set domain type (CPUID.(EAX=0xB,ECX={0}):ECX[15:8]): {1} + DomainType(u32, CheckedAssignError), + /// Failed to set input ECX (CPUID.(EAX=0xB,ECX={0}):ECX[7:0]): {1} + InputEcx(u32, CheckedAssignError), + /// Failed to set number of logical processors (CPUID.(EAX=0xB,ECX={0}):EBX[15:0]): {1} + NumLogicalProcs(u32, CheckedAssignError), + /// Failed to set right-shift bits (CPUID.(EAX=0xB,ECX={0}):EAX[4:0]): {1} + RightShiftBits(u32, CheckedAssignError), + /// Unexpected subleaf: {0} + UnexpectedSubleaf(u32) } /// Error type for setting leaf 0x80000006 of Cpuid::normalize(). @@ -94,66 +95,54 @@ pub fn set_bit(x: &mut u32, bit: u8, y: bool) { } /// Sets a given range to a given value. -#[allow(clippy::arithmetic_side_effects)] pub fn set_range( x: &mut u32, - range: std::ops::Range, + range: std::ops::RangeInclusive, y: u32, ) -> Result<(), CheckedAssignError> { - debug_assert!(range.end >= range.start); - match range.end - range.start { - z @ 0..=31 => { - if y >= 2u32.pow(u32::from(z)) { - Err(CheckedAssignError) - } else { - let shift = y << range.start; - *x = shift | (*x & !mask(range)); - Ok(()) - } - } - 32 => { - let shift = y << range.start; - *x = shift | (*x & !mask(range)); - Ok(()) - } - 33.. => Err(CheckedAssignError), + let start = *range.start(); + let end = *range.end(); + + debug_assert!(end >= start); + debug_assert!(end < 32); + + // Ensure `y` fits within the number of bits in the specified range. + // Note that + // - 1 <= `num_bits` <= 32 from the above assertion + // - if `num_bits` equals to 32, `y` always fits within it since `y` is `u32`. + let num_bits = end - start + 1; + if num_bits < 32 && y >= (1u32 << num_bits) { + return Err(CheckedAssignError); } + + let mask = get_mask(range); + *x = (*x & !mask) | (y << start); + + Ok(()) } + /// Gets a given range within a given value. -#[allow(clippy::arithmetic_side_effects)] -pub fn get_range(x: u32, range: std::ops::Range) -> u32 { - debug_assert!(range.end >= range.start); - (x & mask(range.clone())) >> range.start +pub fn get_range(x: u32, range: std::ops::RangeInclusive) -> u32 { + let start = *range.start(); + let end = *range.end(); + + debug_assert!(end >= start); + debug_assert!(end < 32); + + let mask = get_mask(range); + (x & mask) >> start } /// Returns a mask where the given range is ones. -#[allow( - clippy::as_conversions, - clippy::arithmetic_side_effects, - clippy::cast_possible_truncation -)] -const fn mask(range: std::ops::Range) -> u32 { - /// Returns a value where in the binary representation all bits to the right of the x'th bit - /// from the left are 1. - #[allow(clippy::unreachable)] - const fn shift(x: u8) -> u32 { - if x == 0 { - 0 - } else if x < u32::BITS as u8 { - (1 << x) - 1 - } else if x == u32::BITS as u8 { - u32::MAX - } else { - unreachable!() - } +const fn get_mask(range: std::ops::RangeInclusive) -> u32 { + let num_bits = *range.end() - *range.start() + 1; + let shift = *range.start(); + + if num_bits == 32 { + u32::MAX + } else { + ((1u32 << num_bits) - 1) << shift } - - debug_assert!(range.end >= range.start); - debug_assert!(range.end <= u32::BITS as u8); - - let front = shift(range.start); - let back = shift(range.end); - !front & back } // We use this 2nd implementation so we can conveniently define functions only used within @@ -221,79 +210,54 @@ impl super::Cpuid { cpu_index: u8, cpu_count: u8, ) -> Result<(), FeatureInformationError> { - // Flush a cache line size. - const EBX_CLFLUSH_CACHELINE: u32 = 8; - - // PDCM: Perfmon and Debug Capability. - const ECX_PDCM_BITINDEX: u8 = 15; - - // TSC-Deadline. - const ECX_TSC_DEADLINE_BITINDEX: u8 = 24; - - // CPU is running on a hypervisor. - const ECX_HYPERVISOR_BITINDEX: u8 = 31; - let leaf_1 = self .get_mut(&CpuidKey::leaf(0x1)) .ok_or(FeatureInformationError::MissingLeaf1)?; - // A value of 1 indicates the processor supports the performance and debug feature - // indication MSR IA32_PERF_CAPABILITIES. - // - // pdcm: 15, - set_bit(&mut leaf_1.result.ecx, ECX_PDCM_BITINDEX, false); + // CPUID.01H:EBX[15:08] + // CLFLUSH line size (Value * 8 = cache line size in bytes; used also by CLFLUSHOPT). + set_range(&mut leaf_1.result.ebx, 8..=15, 8).map_err(FeatureInformationError::Clflush)?; - // A value of 1 indicates that the processor’s local APIC timer supports one-shot - // operation using a TSC deadline value. + // CPUID.01H:EBX[23:16] + // Maximum number of addressable IDs for logical processors in this physical package. // - // tsc_deadline: 24, - set_bit(&mut leaf_1.result.ecx, ECX_TSC_DEADLINE_BITINDEX, true); - - // Hypervisor bit - set_bit(&mut leaf_1.result.ecx, ECX_HYPERVISOR_BITINDEX, true); + // The nearest power-of-2 integer that is not smaller than EBX[23:16] is the number of + // unique initial APIC IDs reserved for addressing different logical processors in a + // physical package. This field is only valid if CPUID.1.EDX.HTT[bit 28]= 1. + let max_cpus_per_package = u32::from( + get_max_cpus_per_package(cpu_count) + .map_err(FeatureInformationError::GetMaxCpusPerPackage)?, + ); + set_range(&mut leaf_1.result.ebx, 16..=23, max_cpus_per_package) + .map_err(FeatureInformationError::SetMaxCpusPerPackage)?; + // CPUID.01H:EBX[31:24] // Initial APIC ID. // - // The 8-bit initial APIC ID in EBX[31:24] is replaced by the 32-bit x2APIC ID, - // available in Leaf 0BH and Leaf 1FH. - // - // initial_apic_id: 24..32, - set_range(&mut leaf_1.result.ebx, 24..32, u32::from(cpu_index)) + // The 8-bit initial APIC ID in EBX[31:24] is replaced by the 32-bit x2APIC ID, available + // in Leaf 0BH and Leaf 1FH. + set_range(&mut leaf_1.result.ebx, 24..=31, u32::from(cpu_index)) .map_err(FeatureInformationError::InitialApicId)?; - // CLFLUSH line size (Value ∗ 8 = cache line size in bytes; used also by CLFLUSHOPT). - // - // clflush: 8..16, - set_range(&mut leaf_1.result.ebx, 8..16, EBX_CLFLUSH_CACHELINE) - .map_err(FeatureInformationError::Clflush)?; + // CPUID.01H:ECX[15] (Mnemonic: PDCM) + // Performance and Debug Capability: A value of 1 indicates the processor supports the + // performance and debug feature indication MSR IA32_PERF_CAPABILITIES. + set_bit(&mut leaf_1.result.ecx, 15, false); - let max_cpus_per_package = u32::from( - get_max_cpus_per_package(cpu_count) - .map_err(FeatureInformationError::GetMaxCpusPerPackage)?, - ); + // CPUID.01H:ECX[24] (Mnemonic: TSC-Deadline) + // A value of 1 indicates that the processor’s local APIC timer supports one-shot operation + // using a TSC deadline value. + set_bit(&mut leaf_1.result.ecx, 24, true); - // Maximum number of addressable IDs for logical processors in this physical package. - // - // The nearest power-of-2 integer that is not smaller than EBX[23:16] is the number of - // unique initial APIC IDs reserved for addressing different logical - // processors in a physical package. This field is only valid if - // CPUID.1.EDX.HTT[bit 28]= 1. - // - // max_addressable_logical_processor_ids: 16..24, - set_range(&mut leaf_1.result.ebx, 16..24, max_cpus_per_package) - .map_err(FeatureInformationError::SetMaxCpusPerPackage)?; + // CPUID.01H:ECX[31] (Mnemonic: Hypervisor) + set_bit(&mut leaf_1.result.ecx, 31, true); + // CPUID.01H:EDX[28] (Mnemonic: HTT) // Max APIC IDs reserved field is Valid. A value of 0 for HTT indicates there is only a - // single logical processor in the package and software should assume only a - // single APIC ID is reserved. A value of 1 for HTT indicates the value in - // CPUID.1.EBX[23:16] (the Maximum number of addressable IDs for logical - // processors in this package) is valid for the package. - // - // htt: 28, - - // A value of 1 for HTT indicates the value in CPUID.1.EBX[23:16] - // (the Maximum number of addressable IDs for logical processors in this package) - // is valid for the package + // single logical processor in the package and software should assume only a single APIC ID + // is reserved. A value of 1 for HTT indicates the value in CPUID.1.EBX[23:16] (the Maximum + // number of addressable IDs for logical processors in this package) is valid for the + // package. set_bit(&mut leaf_1.result.edx, 28, cpu_count > 1); Ok(()) @@ -307,17 +271,8 @@ impl super::Cpuid { cpu_bits: u8, cpus_per_core: u8, ) -> Result<(), ExtendedTopologyError> { - /// Level type used for setting thread level processor topology. - const LEVEL_TYPE_THREAD: u32 = 1; - /// Level type used for setting core level processor topology. - const LEVEL_TYPE_CORE: u32 = 2; - /// The APIC ID shift in leaf 0xBh specifies the number of bits to shit the x2APIC ID to - /// get a unique topology of the next level. This allows 128 logical - /// processors/package. - const LEAFBH_INDEX1_APICID: u32 = 7; - // The following commit changed the behavior of KVM_GET_SUPPORTED_CPUID to no longer - // include leaf 0xB / sub-leaf 1. + // include CPUID.(EAX=0BH,ECX=1). // https://lore.kernel.org/all/20221027092036.2698180-1-pbonzini@redhat.com/ self.inner_mut() .entry(CpuidKey::subleaf(0xB, 0x1)) @@ -333,103 +288,95 @@ impl super::Cpuid { for index in 0.. { if let Some(subleaf) = self.get_mut(&CpuidKey::subleaf(0xB, index)) { - // reset eax, ebx, ecx + // Reset eax, ebx, ecx subleaf.result.eax = 0; subleaf.result.ebx = 0; subleaf.result.ecx = 0; - // EDX bits 31..0 contain x2APIC ID of current logical processor - // x2APIC increases the size of the APIC ID from 8 bits to 32 bits + // CPUID.(EAX=0BH,ECX=N).EDX[31:0] + // x2APIC ID of the current logical processor. subleaf.result.edx = u32::from(cpu_index); subleaf.flags = KvmCpuidFlags::SIGNIFICANT_INDEX; - // "If SMT is not present in a processor implementation but CPUID leaf 0BH is - // supported, CPUID.EAX=0BH, ECX=0 will return EAX = 0, EBX = 1 and - // level type = 1. Number of logical processors at the core level is - // reported at level type = 2." (Intel® 64 Architecture x2APIC - // Specification, Ch. 2.8) match index { - // Number of bits to shift right on x2APIC ID to get a unique topology ID of the - // next level type*. All logical processors with the same - // next level ID share current level. + // CPUID.(EAX=0BH,ECX=N):EAX[4:0] + // The number of bits that the x2APIC ID must be shifted to the right to address + // instances of the next higher-scoped domain. When logical processor is not + // supported by the processor, the value of this field at the Logical Processor + // domain sub-leaf may be returned as either 0 (no allocated bits in the x2APIC + // ID) or 1 (one allocated bit in the x2APIC ID); software should plan + // accordingly. + + // CPUID.(EAX=0BH,ECX=N):EBX[15:0] + // The number of logical processors across all instances of this domain within + // the next-higher scoped domain. (For example, in a processor socket/package + // comprising "M" dies of "N" cores each, where each core has "L" logical + // processors, the "die" domain sub-leaf value of this field would be M*N*L.) + // This number reflects configuration as shipped by Intel. Note, software must + // not use this field to enumerate processor topology. + + // CPUID.(EAX=0BH,ECX=N):ECX[7:0] + // The input ECX sub-leaf index. + + // CPUID.(EAX=0BH,ECX=N):ECX[15:8] + // Domain Type. This field provides an identification value which indicates the + // domain as shown below. Although domains are ordered, their assigned + // identification values are not and software should not depend on it. // - // *Software should use this field (EAX[4:0]) to enumerate processor topology of - // the system. + // Hierarchy Domain Domain Type Identification Value + // ----------------------------------------------------------------- + // Lowest Logical Processor 1 + // Highest Core 2 // - // bit_shifts_right_2x_apic_id_unique_topology_id: 0..5 + // (Note that enumeration values of 0 and 3-255 are reserved.) - // Number of logical processors at this level type. The number reflects - // configuration as shipped by Intel**. - // - // **Software must not use EBX[15:0] to enumerate processor topology of the - // system. This value in this field (EBX[15:0]) is only - // intended for display/diagnostic purposes. The actual - // number of logical processors available to BIOS/OS/Applications may be - // different from the value of EBX[15:0], depending on - // software and platform hardware configurations. - // - // logical_processors: 0..16 - - // Level number. Same value in ECX input. - // - // level_number: 0..8, - - // Level type*** - // - // If an input value n in ECX returns the invalid level-type of 0 in ECX[15:8], - // other input values with ECX>n also return 0 in ECX[15:8]. - // - // ***The value of the “level type” field is not related to level numbers in any - // way, higher “level type” values do not mean higher - // levels. Level type field has the following encoding: - // - 0: Invalid. - // - 1: SMT. - // - 2: Core. - // - 3-255: Reserved. - // - // level_type: 8..16 - - // Thread Level Topology; index = 0 + // Logical processor domain 0 => { // To get the next level APIC ID, shift right with at most 1 because we have - // maximum 2 hyperthreads per core that can be represented by 1 bit. - set_range(&mut subleaf.result.eax, 0..5, u32::from(cpu_bits)) - .map_err(ExtendedTopologyError::ApicId)?; + // maximum 2 logical procerssors per core that can be represented by 1 bit. + set_range(&mut subleaf.result.eax, 0..=4, u32::from(cpu_bits)) + .map_err(|err| ExtendedTopologyError::RightShiftBits(index, err))?; // When cpu_count == 1 or HT is disabled, there is 1 logical core at this - // level Otherwise there are 2 - set_range(&mut subleaf.result.ebx, 0..16, u32::from(cpus_per_core)) - .map_err(ExtendedTopologyError::LogicalProcessors)?; + // domain; otherwise there are 2 + set_range(&mut subleaf.result.ebx, 0..=15, u32::from(cpus_per_core)) + .map_err(|err| ExtendedTopologyError::NumLogicalProcs(index, err))?; + + // Skip setting 0 to ECX[7:0] since it's already reset to 0. - set_range(&mut subleaf.result.ecx, 8..16, LEVEL_TYPE_THREAD) - .map_err(ExtendedTopologyError::LevelType)?; + // Set the domain type identification value for logical processor, + set_range(&mut subleaf.result.ecx, 8..=15, 1) + .map_err(|err| ExtendedTopologyError::DomainType(index, err))?; } - // Core Level Processor Topology; index = 1 + // Core domain 1 => { - set_range(&mut subleaf.result.eax, 0..5, LEAFBH_INDEX1_APICID) - .map_err(ExtendedTopologyError::ApicId)?; - - set_range(&mut subleaf.result.ebx, 0..16, u32::from(cpu_count)) - .map_err(ExtendedTopologyError::LogicalProcessors)?; - - // We expect here as this is an extremely rare case that is unlikely to ever - // occur. It would require manual editing of the CPUID structure to push - // more than 2^32 subleaves. - let sub = index; - set_range(&mut subleaf.result.ecx, 0..8, sub) - .map_err(ExtendedTopologyError::LevelNumber)?; - - set_range(&mut subleaf.result.ecx, 8..16, LEVEL_TYPE_CORE) - .map_err(ExtendedTopologyError::LevelType)?; + // Configure such that the next higher-scoped domain (i.e. socket) include + // all logical processors. + // + // The CPUID.(EAX=0BH,ECX=1).EAX[4:0] value must be an integer N such that + // 2^N is greater than or equal to the maximum number of vCPUs. + set_range( + &mut subleaf.result.eax, + 0..=4, + MAX_SUPPORTED_VCPUS.next_power_of_two().ilog2(), + ) + .map_err(|err| ExtendedTopologyError::RightShiftBits(index, err))?; + set_range(&mut subleaf.result.ebx, 0..=15, u32::from(cpu_count)) + .map_err(|err| ExtendedTopologyError::NumLogicalProcs(index, err))?; + + // Setting the input ECX value (i.e. `index`) + set_range(&mut subleaf.result.ecx, 0..=7, index) + .map_err(|err| ExtendedTopologyError::InputEcx(index, err))?; + + // Set the domain type identification value for core. + set_range(&mut subleaf.result.ecx, 8..=15, 2) + .map_err(|err| ExtendedTopologyError::DomainType(index, err))?; } - // Core Level Processor Topology; index >=2 - // No other levels available; This should already be set correctly, - // and it is added here as a "re-enforcement" in case we run on - // different hardware _ => { - // We expect here as this is an extremely rare case that is unlikely to ever - // occur. It would require manual editing of the CPUID structure to push - // more than 2^32 subleaves. - subleaf.result.ecx = index; + // KVM no longer returns any subleaf numbers greater than 0. The patch was + // merged in v6.2 and backported to v5.10. Subleaves >= 2 should not be + // included. + // https://github.com/torvalds/linux/commit/45e966fcca03ecdcccac7cb236e16eea38cc18af + return Err(ExtendedTopologyError::UnexpectedSubleaf(index)); } } } else { diff --git a/src/vmm/src/cpu_config/x86_64/custom_cpu_template.rs b/src/vmm/src/cpu_config/x86_64/custom_cpu_template.rs index 0443d997ead..15ba76abbe4 100644 --- a/src/vmm/src/cpu_config/x86_64/custom_cpu_template.rs +++ b/src/vmm/src/cpu_config/x86_64/custom_cpu_template.rs @@ -15,7 +15,7 @@ use crate::cpu_config::templates::{ use crate::cpu_config::templates_serde::*; use crate::cpu_config::x86_64::cpuid::common::get_vendor_id_from_host; use crate::cpu_config::x86_64::cpuid::{KvmCpuidFlags, VENDOR_ID_AMD, VENDOR_ID_INTEL}; -use crate::cpu_config::x86_64::static_cpu_templates::{c3, t2, t2a, t2cl, t2s, StaticCpuTemplate}; +use crate::cpu_config::x86_64::static_cpu_templates::{StaticCpuTemplate, c3, t2, t2a, t2cl, t2s}; use crate::logger::warn; impl GetCpuTemplate for Option { @@ -192,7 +192,7 @@ where _ => { return Err(D::Error::custom( "Invalid CPUID register. Must be one of [eax, ebx, ecx, edx]", - )) + )); } }) } @@ -214,7 +214,7 @@ mod tests { use serde_json::Value; use super::*; - use crate::cpu_config::x86_64::test_utils::{build_test_template, TEST_TEMPLATE_JSON}; + use crate::cpu_config::x86_64::test_utils::{TEST_TEMPLATE_JSON, build_test_template}; #[test] fn test_get_cpu_template_with_no_template() { @@ -386,10 +386,12 @@ mod tests { ], }"#, ); - assert!(cpu_template_result - .unwrap_err() - .to_string() - .contains("Invalid CPUID register. Must be one of [eax, ebx, ecx, edx]")); + assert!( + cpu_template_result + .unwrap_err() + .to_string() + .contains("Invalid CPUID register. Must be one of [eax, ebx, ecx, edx]") + ); // Malformed MSR register address let cpu_template_result = serde_json::from_str::( @@ -460,10 +462,11 @@ mod tests { ] }"#, ); - assert!(cpu_template_result - .unwrap_err() - .to_string() - .contains("Failed to parse string [0bx00100x0x1xxxx05xxx1xxxxxxxxxxx1] as a bitmap")); + assert!( + cpu_template_result.unwrap_err().to_string().contains( + "Failed to parse string [0bx00100x0x1xxxx05xxx1xxxxxxxxxxx1] as a bitmap" + ) + ); } #[test] diff --git a/src/vmm/src/cpu_config/x86_64/mod.rs b/src/vmm/src/cpu_config/x86_64/mod.rs index 3a26c621194..ddf0b64dea0 100644 --- a/src/vmm/src/cpu_config/x86_64/mod.rs +++ b/src/vmm/src/cpu_config/x86_64/mod.rs @@ -12,8 +12,11 @@ pub mod test_utils; use std::collections::BTreeMap; +use kvm_bindings::CpuId; + use self::custom_cpu_template::CpuidRegister; use super::templates::CustomCpuTemplate; +use crate::Vcpu; use crate::cpu_config::x86_64::cpuid::{Cpuid, CpuidKey}; /// Errors thrown while configuring templates. @@ -24,9 +27,9 @@ pub enum CpuConfigurationError { /// Template changes an MSR entry not supported by KVM: Register Address: {0:0x} MsrNotSupported(u32), /// Can create cpuid from raw: {0} - CpuidFromKvmCpuid(crate::cpu_config::x86_64::cpuid::CpuidTryFromKvmCpuid), + CpuidFromKvmCpuid(#[from] crate::cpu_config::x86_64::cpuid::CpuidTryFromKvmCpuid), /// KVM vcpu ioctl failed: {0} - VcpuIoctl(crate::vstate::vcpu::KvmVcpuError), + VcpuIoctl(#[from] crate::vstate::vcpu::KvmVcpuError), } /// CPU configuration for x86_64 CPUs @@ -41,6 +44,19 @@ pub struct CpuConfiguration { } impl CpuConfiguration { + /// Create new CpuConfiguration. + pub fn new( + supported_cpuid: CpuId, + cpu_template: &CustomCpuTemplate, + first_vcpu: &Vcpu, + ) -> Result { + let cpuid = cpuid::Cpuid::try_from(supported_cpuid)?; + let msrs = first_vcpu + .kvm_vcpu + .get_msrs(cpu_template.msr_index_iter())?; + Ok(CpuConfiguration { cpuid, msrs }) + } + /// Modifies provided config with changes from template pub fn apply_template( self, diff --git a/src/vmm/src/device_manager/acpi.rs b/src/vmm/src/device_manager/acpi.rs index 03315cbcc3c..78f1254d2fa 100644 --- a/src/vmm/src/device_manager/acpi.rs +++ b/src/vmm/src/device_manager/acpi.rs @@ -1,7 +1,7 @@ // Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use acpi_tables::{aml, Aml}; +use acpi_tables::{Aml, aml}; use kvm_ioctls::VmFd; use crate::devices::acpi::vmgenid::VmGenId; diff --git a/src/vmm/src/device_manager/legacy.rs b/src/vmm/src/device_manager/legacy.rs index 45842d933b2..20b008769a5 100644 --- a/src/vmm/src/device_manager/legacy.rs +++ b/src/vmm/src/device_manager/legacy.rs @@ -10,7 +10,7 @@ use std::fmt::Debug; use std::sync::{Arc, Mutex}; use acpi_tables::aml::AmlError; -use acpi_tables::{aml, Aml}; +use acpi_tables::{Aml, aml}; use kvm_ioctls::VmFd; use libc::EFD_NONBLOCK; use vm_superio::Serial; @@ -244,15 +244,12 @@ impl PortIODeviceManager { #[cfg(test)] mod tests { use super::*; - use crate::test_utils::single_region_mem; - use crate::Vm; + use crate::vstate::vm::tests::setup_vm_with_memory; #[test] fn test_register_legacy_devices() { - let guest_mem = single_region_mem(0x1000); - let mut vm = Vm::new(vec![]).unwrap(); - vm.memory_init(&guest_mem, false).unwrap(); - crate::builder::setup_interrupt_controller(&mut vm).unwrap(); + let (_, vm) = setup_vm_with_memory(0x1000); + vm.setup_irqchip().unwrap(); let mut ldm = PortIODeviceManager::new( Arc::new(Mutex::new(BusDevice::Serial(SerialDevice { serial: Serial::with_events( diff --git a/src/vmm/src/device_manager/mmio.rs b/src/vmm/src/device_manager/mmio.rs index 00c155abcfd..99bde6e2e78 100644 --- a/src/vmm/src/device_manager/mmio.rs +++ b/src/vmm/src/device_manager/mmio.rs @@ -7,10 +7,11 @@ use std::collections::HashMap; use std::fmt::Debug; +use std::num::NonZeroU32; use std::sync::{Arc, Mutex}; #[cfg(target_arch = "x86_64")] -use acpi_tables::{aml, Aml}; +use acpi_tables::{Aml, aml}; use kvm_ioctls::{IoEventAddress, VmFd}; use linux_loader::cmdline as kernel_cmdline; #[cfg(target_arch = "x86_64")] @@ -20,10 +21,9 @@ use serde::{Deserialize, Serialize}; use vm_allocator::AllocPolicy; use super::resources::ResourceAllocator; -#[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::DeviceInfoForFDT; use crate::arch::DeviceType; use crate::arch::DeviceType::Virtio; +use crate::devices::BusDevice; #[cfg(target_arch = "aarch64")] use crate::devices::legacy::RTCDevice; use crate::devices::pseudo::BootTimer; @@ -33,9 +33,8 @@ use crate::devices::virtio::device::VirtioDevice; use crate::devices::virtio::mmio::MmioTransport; use crate::devices::virtio::net::Net; use crate::devices::virtio::rng::Entropy; -use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend, TYPE_VSOCK}; +use crate::devices::virtio::vsock::{TYPE_VSOCK, Vsock, VsockUnixBackend}; use crate::devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_NET, TYPE_RNG}; -use crate::devices::BusDevice; #[cfg(target_arch = "x86_64")] use crate::vstate::memory::GuestAddress; @@ -79,8 +78,8 @@ pub struct MMIODeviceInfo { pub addr: u64, /// Mmio addr range length. pub len: u64, - /// Used Irq line(s) for the device. - pub irqs: Vec, + /// Used Irq line for the device. + pub irq: Option, // NOTE: guaranteed to be a value not 0, 0 is not allowed } #[cfg(target_arch = "x86_64")] @@ -150,7 +149,12 @@ impl MMIODeviceManager { resource_allocator: &mut ResourceAllocator, irq_count: u32, ) -> Result { - let irqs = resource_allocator.allocate_gsi(irq_count)?; + let irq = match resource_allocator.allocate_gsi(irq_count)?[..] { + [] => None, + [irq] => NonZeroU32::new(irq), + _ => return Err(MmioError::InvalidIrqConfig), + }; + let device_info = MMIODeviceInfo { addr: resource_allocator.allocate_mmio_memory( MMIO_LEN, @@ -158,7 +162,7 @@ impl MMIODeviceManager { AllocPolicy::FirstMatch, )?, len: MMIO_LEN, - irqs, + irq, }; Ok(device_info) } @@ -187,9 +191,9 @@ impl MMIODeviceManager { ) -> Result<(), MmioError> { // Our virtio devices are currently hardcoded to use a single IRQ. // Validate that requirement. - if device_info.irqs.len() != 1 { + let Some(irq) = device_info.irq else { return Err(MmioError::InvalidIrqConfig); - } + }; let identifier; { let locked_device = mmio_device.locked_device(); @@ -201,11 +205,8 @@ impl MMIODeviceManager { vm.register_ioevent(queue_evt, &io_addr, u32::try_from(i).unwrap()) .map_err(MmioError::RegisterIoEvent)?; } - vm.register_irqfd( - &locked_device.interrupt_trigger().irq_evt, - device_info.irqs[0], - ) - .map_err(MmioError::RegisterIrqFd)?; + vm.register_irqfd(&locked_device.interrupt_trigger().irq_evt, irq.get()) + .map_err(MmioError::RegisterIrqFd)?; } self.register_mmio_device( @@ -230,7 +231,7 @@ impl MMIODeviceManager { .add_virtio_mmio_device( device_info.len, GuestAddress(device_info.addr), - device_info.irqs[0], + device_info.irq.unwrap().get(), None, ) .map_err(MmioError::Cmdline) @@ -257,7 +258,7 @@ impl MMIODeviceManager { device_info.len, // We are sure that `irqs` has at least one element; allocate_mmio_resources makes // sure of it. - device_info.irqs[0], + device_info.irq.unwrap().get(), )?; } Ok(device_info) @@ -289,7 +290,7 @@ impl MMIODeviceManager { .unwrap() .serial .interrupt_evt(), - device_info.irqs[0], + device_info.irq.unwrap().get(), ) .map_err(MmioError::RegisterIrqFd)?; @@ -519,19 +520,6 @@ impl MMIODeviceManager { } } -#[cfg(target_arch = "aarch64")] -impl DeviceInfoForFDT for MMIODeviceInfo { - fn addr(&self) -> u64 { - self.addr - } - fn irq(&self) -> u32 { - self.irqs[0] - } - fn length(&self) -> u64 { - self.len - } -} - #[cfg(test)] mod tests { @@ -540,12 +528,13 @@ mod tests { use vmm_sys_util::eventfd::EventFd; use super::*; + use crate::Vm; + use crate::devices::virtio::ActivateError; use crate::devices::virtio::device::{IrqTrigger, VirtioDevice}; use crate::devices::virtio::queue::Queue; - use crate::devices::virtio::ActivateError; - use crate::test_utils::multi_region_mem; + use crate::test_utils::multi_region_mem_raw; + use crate::vstate::kvm::Kvm; use crate::vstate::memory::{GuestAddress, GuestMemoryMmap}; - use crate::{builder, Vm}; const QUEUE_SIZES: &[u16] = &[64]; @@ -573,11 +562,10 @@ mod tests { #[cfg(target_arch = "x86_64")] /// Gets the number of interrupts used by the devices registered. pub fn used_irqs_count(&self) -> usize { - let mut irq_number = 0; self.get_device_info() .iter() - .for_each(|(_, device_info)| irq_number += device_info.irqs.len()); - irq_number + .filter(|(_, device_info)| device_info.irq.is_some()) + .count() } } @@ -657,26 +645,28 @@ mod tests { } #[test] + #[cfg_attr(target_arch = "x86_64", allow(unused_mut))] fn test_register_virtio_device() { let start_addr1 = GuestAddress(0x0); let start_addr2 = GuestAddress(0x1000); - let guest_mem = multi_region_mem(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]); - let mut vm = Vm::new(vec![]).unwrap(); - vm.memory_init(&guest_mem, false).unwrap(); + let guest_mem = multi_region_mem_raw(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]); + let kvm = Kvm::new(vec![]).expect("Cannot create Kvm"); + let mut vm = Vm::new(&kvm).unwrap(); + vm.register_memory_regions(guest_mem).unwrap(); let mut device_manager = MMIODeviceManager::new(); let mut resource_allocator = ResourceAllocator::new().unwrap(); let mut cmdline = kernel_cmdline::Cmdline::new(4096).unwrap(); let dummy = Arc::new(Mutex::new(DummyDevice::new())); #[cfg(target_arch = "x86_64")] - builder::setup_interrupt_controller(&mut vm).unwrap(); + vm.setup_irqchip().unwrap(); #[cfg(target_arch = "aarch64")] - builder::setup_interrupt_controller(&mut vm, 1).unwrap(); + vm.setup_irqchip(1).unwrap(); device_manager .register_virtio_test_device( vm.fd(), - guest_mem, + vm.guest_memory().clone(), &mut resource_allocator, dummy, &mut cmdline, @@ -686,26 +676,28 @@ mod tests { } #[test] + #[cfg_attr(target_arch = "x86_64", allow(unused_mut))] fn test_register_too_many_devices() { let start_addr1 = GuestAddress(0x0); let start_addr2 = GuestAddress(0x1000); - let guest_mem = multi_region_mem(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]); - let mut vm = Vm::new(vec![]).unwrap(); - vm.memory_init(&guest_mem, false).unwrap(); + let guest_mem = multi_region_mem_raw(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]); + let kvm = Kvm::new(vec![]).expect("Cannot create Kvm"); + let mut vm = Vm::new(&kvm).unwrap(); + vm.register_memory_regions(guest_mem).unwrap(); let mut device_manager = MMIODeviceManager::new(); let mut resource_allocator = ResourceAllocator::new().unwrap(); let mut cmdline = kernel_cmdline::Cmdline::new(4096).unwrap(); #[cfg(target_arch = "x86_64")] - builder::setup_interrupt_controller(&mut vm).unwrap(); + vm.setup_irqchip().unwrap(); #[cfg(target_arch = "aarch64")] - builder::setup_interrupt_controller(&mut vm, 1).unwrap(); + vm.setup_irqchip(1).unwrap(); for _i in crate::arch::IRQ_BASE..=crate::arch::IRQ_MAX { device_manager .register_virtio_test_device( vm.fd(), - guest_mem.clone(), + vm.guest_memory().clone(), &mut resource_allocator, Arc::new(Mutex::new(DummyDevice::new())), &mut cmdline, @@ -719,7 +711,7 @@ mod tests { device_manager .register_virtio_test_device( vm.fd(), - guest_mem, + vm.guest_memory().clone(), &mut resource_allocator, Arc::new(Mutex::new(DummyDevice::new())), &mut cmdline, @@ -740,19 +732,19 @@ mod tests { } #[test] + #[cfg_attr(target_arch = "x86_64", allow(unused_mut))] fn test_device_info() { let start_addr1 = GuestAddress(0x0); let start_addr2 = GuestAddress(0x1000); - let guest_mem = multi_region_mem(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]); - let mut vm = Vm::new(vec![]).unwrap(); - vm.memory_init(&guest_mem, false).unwrap(); - - let mem_clone = guest_mem.clone(); + let guest_mem = multi_region_mem_raw(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]); + let kvm = Kvm::new(vec![]).expect("Cannot create Kvm"); + let mut vm = Vm::new(&kvm).unwrap(); + vm.register_memory_regions(guest_mem).unwrap(); #[cfg(target_arch = "x86_64")] - builder::setup_interrupt_controller(&mut vm).unwrap(); + vm.setup_irqchip().unwrap(); #[cfg(target_arch = "aarch64")] - builder::setup_interrupt_controller(&mut vm, 1).unwrap(); + vm.setup_irqchip(1).unwrap(); let mut device_manager = MMIODeviceManager::new(); let mut resource_allocator = ResourceAllocator::new().unwrap(); @@ -764,36 +756,43 @@ mod tests { let addr = device_manager .register_virtio_test_device( vm.fd(), - guest_mem, + vm.guest_memory().clone(), &mut resource_allocator, dummy, &mut cmdline, &id, ) .unwrap(); - assert!(device_manager - .get_device(DeviceType::Virtio(type_id), &id) - .is_some()); + assert!( + device_manager + .get_device(DeviceType::Virtio(type_id), &id) + .is_some() + ); assert_eq!( addr, device_manager.id_to_dev_info[&(DeviceType::Virtio(type_id), id.clone())].addr ); assert_eq!( crate::arch::IRQ_BASE, - device_manager.id_to_dev_info[&(DeviceType::Virtio(type_id), id)].irqs[0] + device_manager.id_to_dev_info[&(DeviceType::Virtio(type_id), id)] + .irq + .unwrap() + .get() ); let id = "bar"; - assert!(device_manager - .get_device(DeviceType::Virtio(type_id), id) - .is_none()); + assert!( + device_manager + .get_device(DeviceType::Virtio(type_id), id) + .is_none() + ); let dummy2 = Arc::new(Mutex::new(DummyDevice::new())); let id2 = String::from("foo2"); device_manager .register_virtio_test_device( vm.fd(), - mem_clone, + vm.guest_memory().clone(), &mut resource_allocator, dummy2, &mut cmdline, @@ -817,38 +816,31 @@ mod tests { } #[test] - fn test_slot_irq_allocation() { + fn test_no_irq_allocation() { let mut device_manager = MMIODeviceManager::new(); let mut resource_allocator = ResourceAllocator::new().unwrap(); + let device_info = device_manager .allocate_mmio_resources(&mut resource_allocator, 0) .unwrap(); - assert_eq!(device_info.irqs.len(), 0); + assert!(device_info.irq.is_none()); + } + + #[test] + fn test_irq_allocation() { + let mut device_manager = MMIODeviceManager::new(); + let mut resource_allocator = ResourceAllocator::new().unwrap(); + let device_info = device_manager .allocate_mmio_resources(&mut resource_allocator, 1) .unwrap(); - assert_eq!(device_info.irqs[0], crate::arch::IRQ_BASE); - assert_eq!( - format!( - "{}", - device_manager - .allocate_mmio_resources( - &mut resource_allocator, - crate::arch::IRQ_MAX - crate::arch::IRQ_BASE + 1 - ) - .unwrap_err() - ), - "Failed to allocate requested resource: The requested resource is not available." - .to_string() - ); + assert_eq!(device_info.irq.unwrap().get(), crate::arch::IRQ_BASE); + } - let device_info = device_manager - .allocate_mmio_resources( - &mut resource_allocator, - crate::arch::IRQ_MAX - crate::arch::IRQ_BASE - 1, - ) - .unwrap(); - assert_eq!(device_info.irqs[16], crate::arch::IRQ_BASE + 17); + #[test] + fn test_allocation_failure() { + let mut device_manager = MMIODeviceManager::new(); + let mut resource_allocator = ResourceAllocator::new().unwrap(); assert_eq!( format!( "{}", @@ -856,11 +848,7 @@ mod tests { .allocate_mmio_resources(&mut resource_allocator, 2) .unwrap_err() ), - "Failed to allocate requested resource: The requested resource is not available." - .to_string() + "Invalid MMIO IRQ configuration.".to_string() ); - device_manager - .allocate_mmio_resources(&mut resource_allocator, 0) - .unwrap(); } } diff --git a/src/vmm/src/device_manager/persist.rs b/src/vmm/src/device_manager/persist.rs index 5773fa0ba09..30a6387bc82 100644 --- a/src/vmm/src/device_manager/persist.rs +++ b/src/vmm/src/device_manager/persist.rs @@ -15,30 +15,31 @@ use vm_allocator::AllocPolicy; use super::acpi::ACPIDeviceManager; use super::mmio::*; use super::resources::ResourceAllocator; +use crate::EventManager; #[cfg(target_arch = "aarch64")] use crate::arch::DeviceType; use crate::devices::acpi::vmgenid::{VMGenIDState, VMGenIdConstructorArgs, VmGenId, VmGenIdError}; use crate::devices::virtio::balloon::persist::{BalloonConstructorArgs, BalloonState}; use crate::devices::virtio::balloon::{Balloon, BalloonError}; +use crate::devices::virtio::block::BlockError; use crate::devices::virtio::block::device::Block; use crate::devices::virtio::block::persist::{BlockConstructorArgs, BlockState}; -use crate::devices::virtio::block::BlockError; use crate::devices::virtio::device::VirtioDevice; use crate::devices::virtio::mmio::MmioTransport; +use crate::devices::virtio::net::Net; use crate::devices::virtio::net::persist::{ NetConstructorArgs, NetPersistError as NetError, NetState, }; -use crate::devices::virtio::net::Net; use crate::devices::virtio::persist::{MmioTransportConstructorArgs, MmioTransportState}; +use crate::devices::virtio::rng::Entropy; use crate::devices::virtio::rng::persist::{ EntropyConstructorArgs, EntropyPersistError as EntropyError, EntropyState, }; -use crate::devices::virtio::rng::Entropy; use crate::devices::virtio::vsock::persist::{ VsockConstructorArgs, VsockState, VsockUdsConstructorArgs, }; use crate::devices::virtio::vsock::{ - Vsock, VsockError, VsockUnixBackend, VsockUnixBackendError, TYPE_VSOCK, + TYPE_VSOCK, Vsock, VsockError, VsockUnixBackend, VsockUnixBackendError, }; use crate::devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_NET, TYPE_RNG}; use crate::mmds::data_store::MmdsVersion; @@ -46,7 +47,6 @@ use crate::resources::{ResourcesError, VmResources}; use crate::snapshot::Persist; use crate::vmm_config::mmds::MmdsConfigError; use crate::vstate::memory::GuestMemoryMmap; -use crate::EventManager; /// Errors for (de)serialization of the MMIO device manager. #[derive(Debug, thiserror::Error, displaydoc::Display)] @@ -214,6 +214,7 @@ pub struct MMIODevManagerConstructorArgs<'a> { pub resource_allocator: &'a mut ResourceAllocator, pub vm_resources: &'a mut VmResources, pub instance_id: &'a str, + pub restored_from_file: bool, } impl fmt::Debug for MMIODevManagerConstructorArgs<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -512,7 +513,10 @@ impl<'a> Persist<'a> for MMIODeviceManager { if let Some(balloon_state) = &state.balloon_device { let device = Arc::new(Mutex::new(Balloon::restore( - BalloonConstructorArgs { mem: mem.clone() }, + BalloonConstructorArgs { + mem: mem.clone(), + restored_from_file: constructor_args.restored_from_file, + }, &balloon_state.device_state, )?)); @@ -801,12 +805,13 @@ mod tests { let device_states: DeviceStates = Snapshot::deserialize(&mut buf.as_slice()).unwrap(); let vm_resources = &mut VmResources::default(); let restore_args = MMIODevManagerConstructorArgs { - mem: vmm.guest_memory(), + mem: vmm.vm.guest_memory(), vm: vmm.vm.fd(), event_manager: &mut event_manager, resource_allocator: &mut resource_allocator, vm_resources, instance_id: "microvm-id", + restored_from_file: true, }; let restored_dev_manager = MMIODeviceManager::restore(restore_args, &device_states).unwrap(); diff --git a/src/vmm/src/devices/acpi/vmgenid.rs b/src/vmm/src/devices/acpi/vmgenid.rs index 13dd19902df..31dbf64ec39 100644 --- a/src/vmm/src/devices/acpi/vmgenid.rs +++ b/src/vmm/src/devices/acpi/vmgenid.rs @@ -1,7 +1,7 @@ // Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use acpi_tables::{aml, Aml}; +use acpi_tables::{Aml, aml}; use aws_lc_rs::error::Unspecified as RandError; use aws_lc_rs::rand; use log::{debug, error}; diff --git a/src/vmm/src/devices/legacy/i8042.rs b/src/vmm/src/devices/legacy/i8042.rs index 8f965cab912..bcf7bdd8c90 100644 --- a/src/vmm/src/devices/legacy/i8042.rs +++ b/src/vmm/src/devices/legacy/i8042.rs @@ -12,7 +12,7 @@ use log::warn; use serde::Serialize; use vmm_sys_util::eventfd::EventFd; -use crate::logger::{error, IncMetric, SharedIncMetric}; +use crate::logger::{IncMetric, SharedIncMetric, error}; /// Errors thrown by the i8042 device. #[derive(Debug, thiserror::Error, displaydoc::Display)] diff --git a/src/vmm/src/devices/legacy/mod.rs b/src/vmm/src/devices/legacy/mod.rs index 25f332b9617..b28ae7082fe 100644 --- a/src/vmm/src/devices/legacy/mod.rs +++ b/src/vmm/src/devices/legacy/mod.rs @@ -14,8 +14,8 @@ pub mod serial; use std::io; use std::ops::Deref; -use serde::ser::SerializeMap; use serde::Serializer; +use serde::ser::SerializeMap; use vm_superio::Trigger; use vmm_sys_util::eventfd::EventFd; @@ -23,7 +23,7 @@ pub use self::i8042::{I8042Device, I8042Error as I8042DeviceError}; #[cfg(target_arch = "aarch64")] pub use self::rtc_pl031::RTCDevice; pub use self::serial::{ - SerialDevice, SerialEventsWrapper, SerialWrapper, IER_RDA_BIT, IER_RDA_OFFSET, + IER_RDA_BIT, IER_RDA_OFFSET, SerialDevice, SerialEventsWrapper, SerialWrapper, }; /// Wrapper for implementing the trigger functionality for `EventFd`. diff --git a/src/vmm/src/devices/legacy/rtc_pl031.rs b/src/vmm/src/devices/legacy/rtc_pl031.rs index 15e20f81446..754899a23a4 100644 --- a/src/vmm/src/devices/legacy/rtc_pl031.rs +++ b/src/vmm/src/devices/legacy/rtc_pl031.rs @@ -6,7 +6,7 @@ use std::convert::TryInto; use serde::Serialize; use vm_superio::rtc_pl031::RtcEvents; -use crate::logger::{warn, IncMetric, SharedIncMetric}; +use crate::logger::{IncMetric, SharedIncMetric, warn}; /// Metrics specific to the RTC device. #[derive(Debug, Serialize, Default)] diff --git a/src/vmm/src/devices/legacy/serial.rs b/src/vmm/src/devices/legacy/serial.rs index cd74159dbdc..278c15a4464 100644 --- a/src/vmm/src/devices/legacy/serial.rs +++ b/src/vmm/src/devices/legacy/serial.rs @@ -386,7 +386,7 @@ mod tests { ), input: None::, }; - serial.serial.raw_input(&[b'a', b'b', b'c']).unwrap(); + serial.serial.raw_input(b"abc").unwrap(); let invalid_reads_before = metrics.missed_read_count.count(); let mut v = [0x00; 2]; diff --git a/src/vmm/src/devices/virtio/balloon/device.rs b/src/vmm/src/devices/virtio/balloon/device.rs index 697928ae9c6..186f09275bc 100644 --- a/src/vmm/src/devices/virtio/balloon/device.rs +++ b/src/vmm/src/devices/virtio/balloon/device.rs @@ -16,7 +16,7 @@ use super::metrics::METRICS; use super::util::{compact_page_frame_numbers, remove_range}; use super::{ BALLOON_DEV_ID, BALLOON_NUM_QUEUES, BALLOON_QUEUE_SIZES, DEFLATE_INDEX, INFLATE_INDEX, - MAX_PAGES_IN_DESC, MAX_PAGE_COMPACT_BUFFER, MIB_TO_4K_PAGES, STATS_INDEX, + MAX_PAGE_COMPACT_BUFFER, MAX_PAGES_IN_DESC, MIB_TO_4K_PAGES, STATS_INDEX, VIRTIO_BALLOON_F_DEFLATE_ON_OOM, VIRTIO_BALLOON_F_STATS_VQ, VIRTIO_BALLOON_PFN_SHIFT, VIRTIO_BALLOON_S_AVAIL, VIRTIO_BALLOON_S_CACHES, VIRTIO_BALLOON_S_HTLB_PGALLOC, VIRTIO_BALLOON_S_HTLB_PGFAIL, VIRTIO_BALLOON_S_MAJFLT, VIRTIO_BALLOON_S_MEMFREE, @@ -25,7 +25,7 @@ use super::{ }; use crate::devices::virtio::balloon::BalloonError; use crate::devices::virtio::device::{IrqTrigger, IrqType}; -use crate::devices::virtio::gen::virtio_blk::VIRTIO_F_VERSION_1; +use crate::devices::virtio::generated::virtio_config::VIRTIO_F_VERSION_1; use crate::logger::IncMetric; use crate::utils::u64_to_usize; use crate::vstate::memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryMmap}; @@ -164,7 +164,7 @@ pub struct Balloon { pub(crate) irq_trigger: IrqTrigger, // Implementation specific fields. - pub(crate) restored: bool, + pub(crate) restored_from_file: bool, pub(crate) stats_polling_interval_s: u16, pub(crate) stats_timer: TimerFd, // The index of the previous stats descriptor is saved because @@ -189,7 +189,7 @@ impl fmt::Debug for Balloon { .field("queue_evts", &self.queue_evts) .field("device_state", &self.device_state) .field("irq_trigger", &self.irq_trigger) - .field("restored", &self.restored) + .field("restored_from_file", &self.restored_from_file) .field("stats_polling_interval_s", &self.stats_polling_interval_s) .field("stats_desc_index", &self.stats_desc_index) .field("latest_stats", &self.latest_stats) @@ -204,7 +204,7 @@ impl Balloon { amount_mib: u32, deflate_on_oom: bool, stats_polling_interval_s: u16, - restored: bool, + restored_from_file: bool, ) -> Result { let mut avail_features = 1u64 << VIRTIO_F_VERSION_1; @@ -245,7 +245,7 @@ impl Balloon { irq_trigger: IrqTrigger::new().map_err(BalloonError::EventFd)?, device_state: DeviceState::Inactive, activate_evt: EventFd::new(libc::EFD_NONBLOCK).map_err(BalloonError::EventFd)?, - restored, + restored_from_file, stats_polling_interval_s, stats_timer, stats_desc_index: None, @@ -355,7 +355,7 @@ impl Balloon { if let Err(err) = remove_range( mem, (guest_addr, u64::from(range_len) << VIRTIO_BALLOON_PFN_SHIFT), - self.restored, + self.restored_from_file, ) { error!("Error removing memory range: {:?}", err); } @@ -636,7 +636,7 @@ pub(crate) mod tests { check_request_completion, invoke_handler_for_queue_event, set_request, }; use crate::devices::virtio::queue::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE}; - use crate::devices::virtio::test_utils::{default_mem, VirtQueue}; + use crate::devices::virtio::test_utils::{VirtQueue, default_mem}; use crate::test_utils::single_region_mem; use crate::vstate::memory::GuestAddress; diff --git a/src/vmm/src/devices/virtio/balloon/event_handler.rs b/src/vmm/src/devices/virtio/balloon/event_handler.rs index 3019d6877de..56ff5c35047 100644 --- a/src/vmm/src/devices/virtio/balloon/event_handler.rs +++ b/src/vmm/src/devices/virtio/balloon/event_handler.rs @@ -4,7 +4,7 @@ use event_manager::{EventOps, Events, MutEventSubscriber}; use vmm_sys_util::epoll::EventSet; -use super::{report_balloon_event_fail, DEFLATE_INDEX, INFLATE_INDEX, STATS_INDEX}; +use super::{DEFLATE_INDEX, INFLATE_INDEX, STATS_INDEX, report_balloon_event_fail}; use crate::devices::virtio::balloon::device::Balloon; use crate::devices::virtio::device::VirtioDevice; use crate::logger::{error, warn}; @@ -136,7 +136,7 @@ pub mod tests { use super::*; use crate::devices::virtio::balloon::test_utils::set_request; - use crate::devices::virtio::test_utils::{default_mem, VirtQueue}; + use crate::devices::virtio::test_utils::{VirtQueue, default_mem}; use crate::vstate::memory::GuestAddress; #[test] diff --git a/src/vmm/src/devices/virtio/balloon/metrics.rs b/src/vmm/src/devices/virtio/balloon/metrics.rs index da8f7a28520..0b438cae2d4 100644 --- a/src/vmm/src/devices/virtio/balloon/metrics.rs +++ b/src/vmm/src/devices/virtio/balloon/metrics.rs @@ -31,7 +31,7 @@ //! //! The system implements 1 type of metrics: //! * Shared Incremental Metrics (SharedIncMetrics) - dedicated for the metrics which need a counter -//! (i.e the number of times an API request failed). These metrics are reset upon flush. +//! (i.e the number of times an API request failed). These metrics are reset upon flush. use serde::ser::SerializeMap; use serde::{Serialize, Serializer}; diff --git a/src/vmm/src/devices/virtio/balloon/persist.rs b/src/vmm/src/devices/virtio/balloon/persist.rs index 4e768ddd2e2..004fa27f8ca 100644 --- a/src/vmm/src/devices/virtio/balloon/persist.rs +++ b/src/vmm/src/devices/virtio/balloon/persist.rs @@ -3,19 +3,19 @@ //! Defines the structures needed for saving/restoring balloon devices. -use std::sync::atomic::AtomicU32; use std::sync::Arc; +use std::sync::atomic::AtomicU32; use std::time::Duration; use serde::{Deserialize, Serialize}; use timerfd::{SetTimeFlags, TimerState}; use super::*; +use crate::devices::virtio::TYPE_BALLOON; use crate::devices::virtio::balloon::device::{BalloonStats, ConfigSpace}; use crate::devices::virtio::device::DeviceState; use crate::devices::virtio::persist::VirtioDeviceState; use crate::devices::virtio::queue::FIRECRACKER_MAX_QUEUE_SIZE; -use crate::devices::virtio::TYPE_BALLOON; use crate::snapshot::Persist; use crate::vstate::memory::GuestMemoryMmap; @@ -95,6 +95,7 @@ pub struct BalloonState { pub struct BalloonConstructorArgs { /// Pointer to guest memory. pub mem: GuestMemoryMmap, + pub restored_from_file: bool, } impl Persist<'_> for Balloon { @@ -121,7 +122,12 @@ impl Persist<'_> for Balloon { ) -> Result { // We can safely create the balloon with arbitrary flags and // num_pages because we will overwrite them after. - let mut balloon = Balloon::new(0, false, state.stats_polling_interval_s, true)?; + let mut balloon = Balloon::new( + 0, + false, + state.stats_polling_interval_s, + constructor_args.restored_from_file, + )?; let mut num_queues = BALLOON_NUM_QUEUES; // As per the virtio 1.1 specification, the statistics queue @@ -175,9 +181,9 @@ mod tests { use std::sync::atomic::Ordering; use super::*; + use crate::devices::virtio::TYPE_BALLOON; use crate::devices::virtio::device::VirtioDevice; use crate::devices::virtio::test_utils::default_mem; - use crate::devices::virtio::TYPE_BALLOON; use crate::snapshot::Snapshot; #[test] @@ -192,13 +198,16 @@ mod tests { // Deserialize and restore the balloon device. let restored_balloon = Balloon::restore( - BalloonConstructorArgs { mem: guest_mem }, + BalloonConstructorArgs { + mem: guest_mem, + restored_from_file: true, + }, &Snapshot::deserialize(&mut mem.as_slice()).unwrap(), ) .unwrap(); assert_eq!(restored_balloon.device_type(), TYPE_BALLOON); - assert!(restored_balloon.restored); + assert!(restored_balloon.restored_from_file); assert_eq!(restored_balloon.acked_features, balloon.acked_features); assert_eq!(restored_balloon.avail_features, balloon.avail_features); diff --git a/src/vmm/src/devices/virtio/balloon/test_utils.rs b/src/vmm/src/devices/virtio/balloon/test_utils.rs index 8968aa70915..af0d7f5845e 100644 --- a/src/vmm/src/devices/virtio/balloon/test_utils.rs +++ b/src/vmm/src/devices/virtio/balloon/test_utils.rs @@ -5,7 +5,7 @@ use crate::devices::virtio::test_utils::VirtQueue; #[cfg(test)] -use crate::devices::virtio::{balloon::Balloon, balloon::BALLOON_NUM_QUEUES}; +use crate::devices::virtio::{balloon::BALLOON_NUM_QUEUES, balloon::Balloon}; #[cfg(test)] pub fn invoke_handler_for_queue_event(b: &mut Balloon, queue_index: usize) { diff --git a/src/vmm/src/devices/virtio/balloon/util.rs b/src/vmm/src/devices/virtio/balloon/util.rs index f8cf7aa2000..35ef69f972f 100644 --- a/src/vmm/src/devices/virtio/balloon/util.rs +++ b/src/vmm/src/devices/virtio/balloon/util.rs @@ -3,7 +3,7 @@ use std::io; -use super::{RemoveRegionError, MAX_PAGE_COMPACT_BUFFER}; +use super::{MAX_PAGE_COMPACT_BUFFER, RemoveRegionError}; use crate::logger::error; use crate::utils::u64_to_usize; use crate::vstate::memory::{GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion}; @@ -68,7 +68,7 @@ pub(crate) fn compact_page_frame_numbers(v: &mut [u32]) -> Vec<(u32, u32)> { pub(crate) fn remove_range( guest_memory: &GuestMemoryMmap, range: (GuestAddress, u64), - restored: bool, + restored_from_file: bool, ) -> Result<(), RemoveRegionError> { let (guest_address, range_len) = range; @@ -83,7 +83,7 @@ pub(crate) fn remove_range( // Mmap a new anonymous region over the present one in order to create a hole. // This workaround is (only) needed after resuming from a snapshot because the guest memory // is mmaped from file as private and there is no `madvise` flag that works for this case. - if restored { + if restored_from_file { // SAFETY: The address and length are known to be valid. let ret = unsafe { libc::mmap( @@ -125,9 +125,7 @@ mod tests { /// This asserts that $lhs matches $rhs. macro_rules! assert_match { - ($lhs:expr, $rhs:pat) => {{ - assert!(matches!($lhs, $rhs)) - }}; + ($lhs:expr, $rhs:pat) => {{ assert!(matches!($lhs, $rhs)) }}; } #[test] diff --git a/src/vmm/src/devices/virtio/block/device.rs b/src/vmm/src/devices/virtio/block/device.rs index 7399fe39a0b..bf3043bcdd4 100644 --- a/src/vmm/src/devices/virtio/block/device.rs +++ b/src/vmm/src/devices/virtio/block/device.rs @@ -4,10 +4,10 @@ use event_manager::{EventOps, Events, MutEventSubscriber}; use vmm_sys_util::eventfd::EventFd; +use super::BlockError; use super::persist::{BlockConstructorArgs, BlockState}; use super::vhost_user::device::{VhostUserBlock, VhostUserBlockConfig}; use super::virtio::device::{VirtioBlock, VirtioBlockConfig}; -use super::BlockError; use crate::devices::virtio::device::{IrqTrigger, VirtioDevice}; use crate::devices::virtio::queue::Queue; use crate::devices::virtio::{ActivateError, TYPE_BLOCK}; diff --git a/src/vmm/src/devices/virtio/block/vhost_user/device.rs b/src/vmm/src/devices/virtio/block/vhost_user/device.rs index 62218157c8b..b0bf5a31e3f 100644 --- a/src/vmm/src/devices/virtio/block/vhost_user/device.rs +++ b/src/vmm/src/devices/virtio/block/vhost_user/device.rs @@ -7,25 +7,24 @@ use std::sync::Arc; use log::error; -use utils::time::{get_time_us, ClockType}; -use vhost::vhost_user::message::*; +use utils::time::{ClockType, get_time_us}; use vhost::vhost_user::Frontend; +use vhost::vhost_user::message::*; use vmm_sys_util::eventfd::EventFd; -use super::{VhostUserBlockError, NUM_QUEUES, QUEUE_SIZE}; +use super::{NUM_QUEUES, QUEUE_SIZE, VhostUserBlockError}; use crate::devices::virtio::block::CacheType; use crate::devices::virtio::device::{DeviceState, IrqTrigger, IrqType, VirtioDevice}; -use crate::devices::virtio::gen::virtio_blk::{ - VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_RO, VIRTIO_F_VERSION_1, -}; -use crate::devices::virtio::gen::virtio_ring::VIRTIO_RING_F_EVENT_IDX; +use crate::devices::virtio::generated::virtio_blk::{VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_RO}; +use crate::devices::virtio::generated::virtio_config::VIRTIO_F_VERSION_1; +use crate::devices::virtio::generated::virtio_ring::VIRTIO_RING_F_EVENT_IDX; use crate::devices::virtio::queue::Queue; use crate::devices::virtio::vhost_user::{VhostUserHandleBackend, VhostUserHandleImpl}; use crate::devices::virtio::vhost_user_metrics::{ VhostUserDeviceMetrics, VhostUserMetricsPerDevice, }; use crate::devices::virtio::{ActivateError, TYPE_BLOCK}; -use crate::logger::{log_dev_preview_warning, IncMetric, StoreMetric}; +use crate::logger::{IncMetric, StoreMetric, log_dev_preview_warning}; use crate::utils::u64_to_usize; use crate::vmm_config::drive::BlockDeviceConfig; use crate::vstate::memory::GuestMemoryMmap; @@ -377,8 +376,9 @@ mod tests { use super::*; use crate::devices::virtio::block::virtio::device::FileEngineType; use crate::devices::virtio::mmio::VIRTIO_MMIO_INT_CONFIG; + use crate::devices::virtio::vhost_user::tests::create_mem; use crate::test_utils::create_tmp_socket; - use crate::vstate::memory::{FileOffset, GuestAddress, GuestMemoryExtension}; + use crate::vstate::memory::GuestAddress; #[test] fn test_from_config() { @@ -778,12 +778,8 @@ mod tests { let region_size = 0x10000; let file = TempFile::new().unwrap().into_file(); file.set_len(region_size as u64).unwrap(); - let regions = vec![( - FileOffset::new(file.try_clone().unwrap(), 0x0), - GuestAddress(0x0), - region_size, - )]; - let guest_memory = GuestMemoryMmap::from_raw_regions_file(regions, false, false).unwrap(); + let regions = vec![(GuestAddress(0x0), region_size)]; + let guest_memory = create_mem(file, ®ions); // During actiavion of the device features, memory and queues should be set and activated. vhost_block.activate(guest_memory).unwrap(); diff --git a/src/vmm/src/devices/virtio/block/vhost_user/persist.rs b/src/vmm/src/devices/virtio/block/vhost_user/persist.rs index abaa20e012c..d507fa9577b 100644 --- a/src/vmm/src/devices/virtio/block/vhost_user/persist.rs +++ b/src/vmm/src/devices/virtio/block/vhost_user/persist.rs @@ -5,10 +5,10 @@ use serde::{Deserialize, Serialize}; -use super::device::VhostUserBlock; use super::VhostUserBlockError; -use crate::devices::virtio::block::persist::BlockConstructorArgs; +use super::device::VhostUserBlock; use crate::devices::virtio::block::CacheType; +use crate::devices::virtio::block::persist::BlockConstructorArgs; use crate::devices::virtio::persist::VirtioDeviceState; use crate::snapshot::Persist; diff --git a/src/vmm/src/devices/virtio/block/virtio/device.rs b/src/vmm/src/devices/virtio/block/virtio/device.rs index fd352fe2539..e2b134a9f25 100644 --- a/src/vmm/src/devices/virtio/block/virtio/device.rs +++ b/src/vmm/src/devices/virtio/block/virtio/device.rs @@ -8,35 +8,34 @@ use std::cmp; use std::convert::From; use std::fs::{File, OpenOptions}; -use std::io::{Seek, SeekFrom, Write}; +use std::io::{Seek, SeekFrom}; use std::os::linux::fs::MetadataExt; use std::path::PathBuf; use std::sync::Arc; use block_io::FileEngine; use serde::{Deserialize, Serialize}; +use vm_memory::ByteValued; use vmm_sys_util::eventfd::EventFd; use super::io::async_io; use super::request::*; -use super::{ - io as block_io, VirtioBlockError, BLOCK_CONFIG_SPACE_SIZE, BLOCK_QUEUE_SIZES, SECTOR_SHIFT, - SECTOR_SIZE, -}; -use crate::devices::virtio::block::virtio::metrics::{BlockDeviceMetrics, BlockMetricsPerDevice}; +use super::{BLOCK_QUEUE_SIZES, SECTOR_SHIFT, SECTOR_SIZE, VirtioBlockError, io as block_io}; use crate::devices::virtio::block::CacheType; +use crate::devices::virtio::block::virtio::metrics::{BlockDeviceMetrics, BlockMetricsPerDevice}; use crate::devices::virtio::device::{DeviceState, IrqTrigger, IrqType, VirtioDevice}; -use crate::devices::virtio::gen::virtio_blk::{ - VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_RO, VIRTIO_BLK_ID_BYTES, VIRTIO_F_VERSION_1, +use crate::devices::virtio::generated::virtio_blk::{ + VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_RO, VIRTIO_BLK_ID_BYTES, }; -use crate::devices::virtio::gen::virtio_ring::VIRTIO_RING_F_EVENT_IDX; +use crate::devices::virtio::generated::virtio_config::VIRTIO_F_VERSION_1; +use crate::devices::virtio::generated::virtio_ring::VIRTIO_RING_F_EVENT_IDX; use crate::devices::virtio::queue::Queue; use crate::devices::virtio::{ActivateError, TYPE_BLOCK}; -use crate::logger::{error, warn, IncMetric}; +use crate::logger::{IncMetric, error, warn}; use crate::rate_limiter::{BucketUpdate, RateLimiter}; use crate::utils::u64_to_usize; -use crate::vmm_config::drive::BlockDeviceConfig; use crate::vmm_config::RateLimiterConfig; +use crate::vmm_config::drive::BlockDeviceConfig; use crate::vstate::memory::GuestMemoryMmap; /// The engine file type, either Sync or Async (through io_uring). @@ -155,20 +154,17 @@ impl DiskProperties { } default_id } +} - /// Provides vec containing the virtio block configuration space - /// buffer. The config space is populated with the disk size based - /// on the backing file size. - pub fn virtio_block_config_space(&self) -> Vec { - // The config space is little endian. - let mut config = Vec::with_capacity(BLOCK_CONFIG_SPACE_SIZE); - for i in 0..BLOCK_CONFIG_SPACE_SIZE { - config.push(((self.nsectors >> (8 * i)) & 0xff) as u8); - } - config - } +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] +#[repr(C)] +pub struct ConfigSpace { + pub capacity: u64, } +// SAFETY: `ConfigSpace` contains only PODs in `repr(C)` or `repr(transparent)`, without padding. +unsafe impl ByteValued for ConfigSpace {} + /// Use this structure to set up the Block Device before booting the kernel. #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(deny_unknown_fields)] @@ -246,7 +242,7 @@ pub struct VirtioBlock { // Virtio fields. pub avail_features: u64, pub acked_features: u64, - pub config_space: Vec, + pub config_space: ConfigSpace, pub activate_evt: EventFd, // Transport related fields. @@ -313,10 +309,14 @@ impl VirtioBlock { let queues = BLOCK_QUEUE_SIZES.iter().map(|&s| Queue::new(s)).collect(); + let config_space = ConfigSpace { + capacity: disk_properties.nsectors.to_le(), + }; + Ok(VirtioBlock { avail_features, acked_features: 0u64, - config_space: disk_properties.virtio_block_config_space(), + config_space, activate_evt: EventFd::new(libc::EFD_NONBLOCK).map_err(VirtioBlockError::EventFd)?, queues, @@ -524,7 +524,7 @@ impl VirtioBlock { /// Update the backing file and the config space of the block device. pub fn update_disk_image(&mut self, disk_image_path: String) -> Result<(), VirtioBlockError> { self.disk.update(disk_image_path, self.read_only)?; - self.config_space = self.disk.virtio_block_config_space(); + self.config_space.capacity = self.disk.nsectors.to_le(); // virtio_block_config_space(); // Kick the driver to pick up the changes. self.irq_trigger.trigger_irq(IrqType::Config).unwrap(); @@ -598,28 +598,23 @@ impl VirtioDevice for VirtioBlock { &self.irq_trigger } - fn read_config(&self, offset: u64, mut data: &mut [u8]) { - let config_len = self.config_space.len() as u64; - if offset >= config_len { + fn read_config(&self, offset: u64, data: &mut [u8]) { + if let Some(config_space_bytes) = self.config_space.as_slice().get(u64_to_usize(offset)..) { + let len = config_space_bytes.len().min(data.len()); + data[..len].copy_from_slice(&config_space_bytes[..len]); + } else { error!("Failed to read config space"); self.metrics.cfg_fails.inc(); - return; - } - if let Some(end) = offset.checked_add(data.len() as u64) { - // This write can't fail, offset and end are checked against config_len. - data.write_all( - &self.config_space[u64_to_usize(offset)..u64_to_usize(cmp::min(end, config_len))], - ) - .unwrap(); } } fn write_config(&mut self, offset: u64, data: &[u8]) { + let config_space_bytes = self.config_space.as_mut_slice(); let start = usize::try_from(offset).ok(); let end = start.and_then(|s| s.checked_add(data.len())); let Some(dst) = start .zip(end) - .and_then(|(start, end)| self.config_space.get_mut(start..end)) + .and_then(|(start, end)| config_space_bytes.get_mut(start..end)) else { error!("Failed to write config space"); self.metrics.cfg_fails.inc(); @@ -673,7 +668,7 @@ impl Drop for VirtioBlock { #[cfg(test)] mod tests { use std::fs::metadata; - use std::io::Read; + use std::io::{Read, Write}; use std::os::unix::ffi::OsStrExt; use std::thread; use std::time::Duration; @@ -682,14 +677,14 @@ mod tests { use super::*; use crate::check_metric_after_block; + use crate::devices::virtio::block::virtio::IO_URING_NUM_ENTRIES; use crate::devices::virtio::block::virtio::test_utils::{ default_block, read_blk_req_descriptors, set_queue, set_rate_limiter, simulate_async_completion_event, simulate_queue_and_async_completion_events, simulate_queue_event, }; - use crate::devices::virtio::block::virtio::IO_URING_NUM_ENTRIES; use crate::devices::virtio::queue::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE}; - use crate::devices::virtio::test_utils::{default_mem, VirtQueue}; + use crate::devices::virtio::test_utils::{VirtQueue, default_mem}; use crate::rate_limiter::TokenType; use crate::vstate::memory::{Address, Bytes, GuestAddress}; @@ -755,11 +750,6 @@ mod tests { assert_eq!(size, u64::from(SECTOR_SIZE) * num_sectors); assert_eq!(disk_properties.nsectors, num_sectors); - let cfg = disk_properties.virtio_block_config_space(); - assert_eq!(cfg.len(), BLOCK_CONFIG_SPACE_SIZE); - for (i, byte) in cfg.iter().enumerate() { - assert_eq!(*byte, ((num_sectors >> (8 * i)) & 0xff) as u8); - } // Testing `backing_file.virtio_block_disk_image_id()` implies // duplicating that logic in tests, so skipping it. @@ -803,20 +793,21 @@ mod tests { for engine in [FileEngineType::Sync, FileEngineType::Async] { let block = default_block(engine); - let mut actual_config_space = [0u8; BLOCK_CONFIG_SPACE_SIZE]; - block.read_config(0, &mut actual_config_space); + let mut actual_config_space = ConfigSpace::default(); + block.read_config(0, actual_config_space.as_mut_slice()); // This will read the number of sectors. // The block's backing file size is 0x1000, so there are 8 (4096/512) sectors. // The config space is little endian. - let expected_config_space: [u8; BLOCK_CONFIG_SPACE_SIZE] = - [0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let expected_config_space = ConfigSpace { capacity: 8 }; assert_eq!(actual_config_space, expected_config_space); // Invalid read. - let expected_config_space: [u8; BLOCK_CONFIG_SPACE_SIZE] = - [0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf]; + let expected_config_space = ConfigSpace { capacity: 696969 }; actual_config_space = expected_config_space; - block.read_config(BLOCK_CONFIG_SPACE_SIZE as u64 + 1, &mut actual_config_space); + block.read_config( + std::mem::size_of::() as u64 + 1, + actual_config_space.as_mut_slice(), + ); // Validate read failed (the config space was not updated). assert_eq!(actual_config_space, expected_config_space); @@ -828,33 +819,37 @@ mod tests { for engine in [FileEngineType::Sync, FileEngineType::Async] { let mut block = default_block(engine); - let expected_config_space: [u8; BLOCK_CONFIG_SPACE_SIZE] = - [0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - block.write_config(0, &expected_config_space); + let expected_config_space = ConfigSpace { capacity: 696969 }; + block.write_config(0, expected_config_space.as_slice()); - let mut actual_config_space = [0u8; BLOCK_CONFIG_SPACE_SIZE]; - block.read_config(0, &mut actual_config_space); + let mut actual_config_space = ConfigSpace::default(); + block.read_config(0, actual_config_space.as_mut_slice()); assert_eq!(actual_config_space, expected_config_space); // If priviledged user writes to `/dev/mem`, in block config space - byte by byte. - let expected_config_space = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x00, 0x11]; - for i in 0..expected_config_space.len() { - block.write_config(i as u64, &expected_config_space[i..=i]); + let expected_config_space = ConfigSpace { + capacity: 0x1122334455667788, + }; + let expected_config_space_slice = expected_config_space.as_slice(); + for (i, b) in expected_config_space_slice.iter().enumerate() { + block.write_config(i as u64, &[*b]); } - block.read_config(0, &mut actual_config_space); + block.read_config(0, actual_config_space.as_mut_slice()); assert_eq!(actual_config_space, expected_config_space); // Invalid write. - let new_config_space = [0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf]; - block.write_config(5, &new_config_space); + let new_config_space = ConfigSpace { + capacity: 0xDEADBEEF, + }; + block.write_config(5, new_config_space.as_slice()); // Make sure nothing got written. - block.read_config(0, &mut actual_config_space); + block.read_config(0, actual_config_space.as_mut_slice()); assert_eq!(actual_config_space, expected_config_space); // Large offset that may cause an overflow. - block.write_config(u64::MAX, &new_config_space); + block.write_config(u64::MAX, new_config_space.as_slice()); // Make sure nothing got written. - block.read_config(0, &mut actual_config_space); + block.read_config(0, actual_config_space.as_mut_slice()); assert_eq!(actual_config_space, expected_config_space); } } diff --git a/src/vmm/src/devices/virtio/block/virtio/event_handler.rs b/src/vmm/src/devices/virtio/block/virtio/event_handler.rs index 8400766e06b..db69e23d7f0 100644 --- a/src/vmm/src/devices/virtio/block/virtio/event_handler.rs +++ b/src/vmm/src/devices/virtio/block/virtio/event_handler.rs @@ -124,7 +124,7 @@ mod tests { }; use crate::devices::virtio::block::virtio::{VIRTIO_BLK_S_OK, VIRTIO_BLK_T_OUT}; use crate::devices::virtio::queue::VIRTQ_DESC_F_NEXT; - use crate::devices::virtio::test_utils::{default_mem, VirtQueue}; + use crate::devices::virtio::test_utils::{VirtQueue, default_mem}; use crate::vstate::memory::{Bytes, GuestAddress}; #[test] diff --git a/src/vmm/src/devices/virtio/block/virtio/io/async_io.rs b/src/vmm/src/devices/virtio/block/virtio/io/async_io.rs index 515ff9ca32c..3073b55886a 100644 --- a/src/vmm/src/devices/virtio/block/virtio/io/async_io.rs +++ b/src/vmm/src/devices/virtio/block/virtio/io/async_io.rs @@ -9,8 +9,8 @@ use std::os::unix::io::AsRawFd; use vm_memory::GuestMemoryError; use vmm_sys_util::eventfd::EventFd; -use crate::devices::virtio::block::virtio::io::UserDataError; use crate::devices::virtio::block::virtio::IO_URING_NUM_ENTRIES; +use crate::devices::virtio::block::virtio::io::UserDataError; use crate::io_uring::operation::{Cqe, OpCode, Operation}; use crate::io_uring::restriction::Restriction; use crate::io_uring::{self, IoUring, IoUringError}; diff --git a/src/vmm/src/devices/virtio/block/virtio/io/mod.rs b/src/vmm/src/devices/virtio/block/virtio/io/mod.rs index de970986da2..79fe7c0c77d 100644 --- a/src/vmm/src/devices/virtio/block/virtio/io/mod.rs +++ b/src/vmm/src/devices/virtio/block/virtio/io/mod.rs @@ -187,7 +187,6 @@ impl FileEngine { pub mod tests { #![allow(clippy::undocumented_unsafe_blocks)] use std::os::unix::ffi::OsStrExt; - use std::os::unix::io::FromRawFd; use vmm_sys_util::tempfile::TempFile; @@ -195,26 +194,13 @@ pub mod tests { use crate::devices::virtio::block::virtio::device::FileEngineType; use crate::utils::u64_to_usize; use crate::vmm_config::machine_config::HugePageConfig; - use crate::vstate::memory::{Bitmap, Bytes, GuestMemory, GuestMemoryExtension}; + use crate::vstate::memory; + use crate::vstate::memory::{Bitmap, Bytes, GuestMemory}; const FILE_LEN: u32 = 1024; // 2 pages of memory should be enough to test read/write ops and also dirty tracking. const MEM_LEN: usize = 8192; - macro_rules! assert_err { - ($expression:expr, $($pattern:tt)+) => { - match $expression { - Err(UserDataError { - user_data: _, - error: $($pattern)+, - }) => (), - ref err => { - panic!("expected `{}` but got `{:?}`", stringify!($($pattern)+), err); - } - } - }; - } - macro_rules! assert_sync_execution { ($expression:expr, $count:expr) => { match $expression { @@ -238,15 +224,22 @@ pub mod tests { } fn assert_async_execution(mem: &GuestMemoryMmap, engine: &mut FileEngine<()>, count: u32) { - if let FileEngine::Async(ref mut engine) = engine { + if let FileEngine::Async(engine) = engine { engine.drain(false).unwrap(); assert_eq!(engine.pop(mem).unwrap().unwrap().result().unwrap(), count); } } fn create_mem() -> GuestMemoryMmap { - GuestMemoryMmap::from_raw_regions(&[(GuestAddress(0), MEM_LEN)], true, HugePageConfig::None) - .unwrap() + GuestMemoryMmap::from_regions( + memory::anonymous( + [(GuestAddress(0), MEM_LEN)].into_iter(), + true, + HugePageConfig::None, + ) + .unwrap(), + ) + .unwrap() } fn check_dirty_mem(mem: &GuestMemoryMmap, addr: GuestAddress, len: u32) { @@ -265,17 +258,7 @@ pub mod tests { #[test] fn test_sync() { - // Check invalid file let mem = create_mem(); - let file = unsafe { File::from_raw_fd(-2) }; - let mut engine = FileEngine::from_file(file, FileEngineType::Sync).unwrap(); - let res = engine.read(0, &mem, GuestAddress(0), 0, ()); - assert_err!(res, BlockIoError::Sync(sync_io::SyncIoError::Seek(_e))); - let res = engine.write(0, &mem, GuestAddress(0), 0, ()); - assert_err!(res, BlockIoError::Sync(sync_io::SyncIoError::Seek(_e))); - let res = engine.flush(()); - assert_err!(res, BlockIoError::Sync(sync_io::SyncIoError::SyncAll(_e))); - // Create backing file. let file = TempFile::new().unwrap().into_file(); let mut engine = FileEngine::from_file(file, FileEngineType::Sync).unwrap(); @@ -342,10 +325,6 @@ pub mod tests { #[test] fn test_async() { - // Check invalid file - let file = unsafe { File::from_raw_fd(-2) }; - FileEngine::<()>::from_file(file, FileEngineType::Async).unwrap_err(); - // Create backing file. let file = TempFile::new().unwrap().into_file(); let mut engine = FileEngine::<()>::from_file(file, FileEngineType::Async).unwrap(); diff --git a/src/vmm/src/devices/virtio/block/virtio/metrics.rs b/src/vmm/src/devices/virtio/block/virtio/metrics.rs index 69a521fde54..0abe2a58963 100644 --- a/src/vmm/src/devices/virtio/block/virtio/metrics.rs +++ b/src/vmm/src/devices/virtio/block/virtio/metrics.rs @@ -72,7 +72,8 @@ //! //! The system implements 1 type of metrics: //! * Shared Incremental Metrics (SharedIncMetrics) - dedicated for the metrics which need a counter -//! (i.e the number of times an API request failed). These metrics are reset upon flush. +//! (i.e the number of times an API request failed). These metrics are reset upon flush. +//! //! We add BlockDeviceMetrics entries from block::metrics::METRICS into Block device instead of //! Block device having individual separate BlockDeviceMetrics entries because Block device is not //! accessible from signal handlers to flush metrics and block::metrics::METRICS is. diff --git a/src/vmm/src/devices/virtio/block/virtio/mod.rs b/src/vmm/src/devices/virtio/block/virtio/mod.rs index 8a2045f19e5..8ea59a5aba4 100644 --- a/src/vmm/src/devices/virtio/block/virtio/mod.rs +++ b/src/vmm/src/devices/virtio/block/virtio/mod.rs @@ -18,8 +18,6 @@ pub use self::request::*; pub use crate::devices::virtio::block::CacheType; use crate::devices::virtio::queue::FIRECRACKER_MAX_QUEUE_SIZE; -/// Size of config space for block device. -pub const BLOCK_CONFIG_SPACE_SIZE: usize = 8; /// Sector shift for block device. pub const SECTOR_SHIFT: u8 = 9; /// Size of block sector. diff --git a/src/vmm/src/devices/virtio/block/virtio/persist.rs b/src/vmm/src/devices/virtio/block/virtio/persist.rs index 61bffbeaa40..8c6f2c2453d 100644 --- a/src/vmm/src/devices/virtio/block/virtio/persist.rs +++ b/src/vmm/src/devices/virtio/block/virtio/persist.rs @@ -3,23 +3,24 @@ //! Defines the structures needed for saving/restoring block devices. -use std::sync::atomic::AtomicU32; use std::sync::Arc; +use std::sync::atomic::AtomicU32; +use device::ConfigSpace; use serde::{Deserialize, Serialize}; use vmm_sys_util::eventfd::EventFd; use super::device::DiskProperties; use super::*; +use crate::devices::virtio::TYPE_BLOCK; use crate::devices::virtio::block::persist::BlockConstructorArgs; use crate::devices::virtio::block::virtio::device::FileEngineType; use crate::devices::virtio::block::virtio::metrics::BlockMetricsPerDevice; use crate::devices::virtio::device::{DeviceState, IrqTrigger}; -use crate::devices::virtio::gen::virtio_blk::VIRTIO_BLK_F_RO; +use crate::devices::virtio::generated::virtio_blk::VIRTIO_BLK_F_RO; use crate::devices::virtio::persist::VirtioDeviceState; -use crate::devices::virtio::TYPE_BLOCK; -use crate::rate_limiter::persist::RateLimiterState; use crate::rate_limiter::RateLimiter; +use crate::rate_limiter::persist::RateLimiterState; use crate::snapshot::Persist; /// Holds info about block's file engine type. Gets saved in snapshot. @@ -122,10 +123,14 @@ impl Persist<'_> for VirtioBlock { DeviceState::Inactive }; + let config_space = ConfigSpace { + capacity: disk_properties.nsectors.to_le(), + }; + Ok(VirtioBlock { avail_features, acked_features, - config_space: disk_properties.virtio_block_config_space(), + config_space, activate_evt: EventFd::new(libc::EFD_NONBLOCK).map_err(VirtioBlockError::EventFd)?, queues, diff --git a/src/vmm/src/devices/virtio/block/virtio/request.rs b/src/vmm/src/devices/virtio/block/virtio/request.rs index 4eb8d7e2efd..f8b3172c5c4 100644 --- a/src/vmm/src/devices/virtio/block/virtio/request.rs +++ b/src/vmm/src/devices/virtio/block/virtio/request.rs @@ -9,15 +9,15 @@ use std::convert::From; use vm_memory::GuestMemoryError; -use super::{io as block_io, VirtioBlockError, SECTOR_SHIFT, SECTOR_SIZE}; +use super::{SECTOR_SHIFT, SECTOR_SIZE, VirtioBlockError, io as block_io}; use crate::devices::virtio::block::virtio::device::DiskProperties; use crate::devices::virtio::block::virtio::metrics::BlockDeviceMetrics; -pub use crate::devices::virtio::gen::virtio_blk::{ +pub use crate::devices::virtio::generated::virtio_blk::{ VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, }; use crate::devices::virtio::queue::DescriptorChain; -use crate::logger::{error, IncMetric}; +use crate::logger::{IncMetric, error}; use crate::rate_limiter::{RateLimiter, TokenType}; use crate::vstate::memory::{ByteValued, Bytes, GuestAddress, GuestMemoryMmap}; @@ -421,7 +421,7 @@ mod tests { use super::*; use crate::devices::virtio::queue::{Queue, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE}; - use crate::devices::virtio::test_utils::{default_mem, VirtQueue}; + use crate::devices::virtio::test_utils::{VirtQueue, default_mem}; use crate::vstate::memory::{Address, GuestAddress, GuestMemory}; const NUM_DISK_SECTORS: u64 = 1024; @@ -467,7 +467,7 @@ mod tests { assert_eq!(RequestType::from(42), RequestType::Unsupported(42)); } - impl<'a, 'b> RequestDescriptorChain<'a, 'b> { + impl RequestDescriptorChain<'_, '_> { fn check_parse_err(&self, _e: VirtioBlockError) { let mut q = self.driver_queue.create_queue(); let memory = self.driver_queue.memory(); @@ -736,8 +736,8 @@ mod tests { } #[allow(clippy::let_with_type_underscore)] - fn random_request_parse( - ) -> impl Strategy, GuestMemoryMmap, Queue)> { + fn random_request_parse() + -> impl Strategy, GuestMemoryMmap, Queue)> { // In this strategy we are going to generate random Requests/Errors and map them // to an input descriptor chain. // diff --git a/src/vmm/src/devices/virtio/block/virtio/test_utils.rs b/src/vmm/src/devices/virtio/block/virtio/test_utils.rs index 106da8177cd..02dd34fbce9 100644 --- a/src/vmm/src/devices/virtio/block/virtio/test_utils.rs +++ b/src/vmm/src/devices/virtio/block/virtio/test_utils.rs @@ -10,8 +10,8 @@ use std::time::Duration; use vmm_sys_util::tempfile::TempFile; -use super::device::VirtioBlockConfig; use super::RequestHeader; +use super::device::VirtioBlockConfig; use crate::devices::virtio::block::virtio::device::FileEngineType; #[cfg(test)] use crate::devices::virtio::block::virtio::io::FileEngine; diff --git a/src/vmm/src/devices/virtio/device.rs b/src/vmm/src/devices/virtio/device.rs index b5af6862af9..62131e775f5 100644 --- a/src/vmm/src/devices/virtio/device.rs +++ b/src/vmm/src/devices/virtio/device.rs @@ -6,14 +6,14 @@ // found in the THIRD-PARTY file. use std::fmt; -use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicU32, Ordering}; use vmm_sys_util::eventfd::EventFd; +use super::ActivateError; use super::mmio::{VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING}; use super::queue::{Queue, QueueError}; -use super::ActivateError; use crate::devices::virtio::AsAny; use crate::logger::{error, warn}; use crate::vstate::memory::GuestMemoryMmap; @@ -38,7 +38,7 @@ impl DeviceState { /// Gets the memory attached to the device if it is activated. pub fn mem(&self) -> Option<&GuestMemoryMmap> { match self { - DeviceState::Activated(ref mem) => Some(mem), + DeviceState::Activated(mem) => Some(mem), DeviceState::Inactive => None, } } @@ -104,7 +104,7 @@ pub trait VirtioDevice: AsAny + Send { /// Check if virtio device has negotiated given feature. fn has_feature(&self, feature: u64) -> bool { - (self.acked_features() & 1 << feature) != 0 + (self.acked_features() & (1 << feature)) != 0 } /// The virtio device type. @@ -156,7 +156,7 @@ pub trait VirtioDevice: AsAny + Send { let avail_features = self.avail_features(); let unrequested_features = v & !avail_features; if unrequested_features != 0 { - warn!("Received acknowledge request for unknown feature: {:x}", v); + warn!("Received acknowledge request for unknown feature: {:#x}", v); // Don't count these features as acked. v &= !unrequested_features; } diff --git a/src/vmm/src/devices/virtio/gen/virtio_net.rs b/src/vmm/src/devices/virtio/gen/virtio_net.rs deleted file mode 100644 index 156e25497fe..00000000000 --- a/src/vmm/src/devices/virtio/gen/virtio_net.rs +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// automatically generated by tools/bindgen.sh - -#![allow( - non_camel_case_types, - non_upper_case_globals, - dead_code, - non_snake_case, - clippy::ptr_as_ptr, - clippy::undocumented_unsafe_blocks, - missing_debug_implementations, - clippy::tests_outside_test_module -)] - -pub const VIRTIO_F_NOTIFY_ON_EMPTY: u32 = 24; -pub const VIRTIO_F_ANY_LAYOUT: u32 = 27; -pub const VIRTIO_F_VERSION_1: u32 = 32; -pub const VIRTIO_F_ACCESS_PLATFORM: u32 = 33; -pub const VIRTIO_F_IOMMU_PLATFORM: u32 = 33; -pub const VIRTIO_F_RING_PACKED: u32 = 34; -pub const VIRTIO_F_ORDER_PLATFORM: u32 = 36; -pub const VIRTIO_F_SR_IOV: u32 = 37; -pub const VIRTIO_NET_F_CSUM: u32 = 0; -pub const VIRTIO_NET_F_GUEST_CSUM: u32 = 1; -pub const VIRTIO_NET_F_CTRL_GUEST_OFFLOADS: u32 = 2; -pub const VIRTIO_NET_F_MTU: u32 = 3; -pub const VIRTIO_NET_F_MAC: u32 = 5; -pub const VIRTIO_NET_F_GUEST_TSO4: u32 = 7; -pub const VIRTIO_NET_F_GUEST_TSO6: u32 = 8; -pub const VIRTIO_NET_F_GUEST_ECN: u32 = 9; -pub const VIRTIO_NET_F_GUEST_UFO: u32 = 10; -pub const VIRTIO_NET_F_HOST_TSO4: u32 = 11; -pub const VIRTIO_NET_F_HOST_TSO6: u32 = 12; -pub const VIRTIO_NET_F_HOST_ECN: u32 = 13; -pub const VIRTIO_NET_F_HOST_UFO: u32 = 14; -pub const VIRTIO_NET_F_MRG_RXBUF: u32 = 15; -pub const VIRTIO_NET_F_STATUS: u32 = 16; -pub const VIRTIO_NET_F_CTRL_VQ: u32 = 17; -pub const VIRTIO_NET_F_CTRL_RX: u32 = 18; -pub const VIRTIO_NET_F_CTRL_VLAN: u32 = 19; -pub const VIRTIO_NET_F_CTRL_RX_EXTRA: u32 = 20; -pub const VIRTIO_NET_F_GUEST_ANNOUNCE: u32 = 21; -pub const VIRTIO_NET_F_MQ: u32 = 22; -pub const VIRTIO_NET_F_CTRL_MAC_ADDR: u32 = 23; -pub const VIRTIO_NET_F_HASH_REPORT: u32 = 57; -pub const VIRTIO_NET_F_RSS: u32 = 60; -pub const VIRTIO_NET_F_RSC_EXT: u32 = 61; -pub const VIRTIO_NET_F_STANDBY: u32 = 62; -pub const VIRTIO_NET_F_SPEED_DUPLEX: u32 = 63; -pub const VIRTIO_NET_F_GSO: u32 = 6; -pub type __u8 = ::std::os::raw::c_uchar; -pub type __u16 = ::std::os::raw::c_ushort; -pub type __le16 = __u16; -pub type __virtio16 = __u16; -#[repr(C)] -#[derive(Copy, Clone)] -pub struct virtio_net_hdr_v1 { - pub flags: __u8, - pub gso_type: __u8, - pub hdr_len: __virtio16, - pub gso_size: __virtio16, - pub __bindgen_anon_1: virtio_net_hdr_v1__bindgen_ty_1, - pub num_buffers: __virtio16, -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union virtio_net_hdr_v1__bindgen_ty_1 { - pub __bindgen_anon_1: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1, - pub csum: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2, - pub rsc: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3, -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1 { - pub csum_start: __virtio16, - pub csum_offset: __virtio16, -} -#[test] -fn bindgen_test_layout_virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 4usize, - concat!( - "Size of: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1) - ) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!( - "Alignment of ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).csum_start) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1), - "::", - stringify!(csum_start) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).csum_offset) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1), - "::", - stringify!(csum_offset) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2 { - pub start: __virtio16, - pub offset: __virtio16, -} -#[test] -fn bindgen_test_layout_virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 4usize, - concat!( - "Size of: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2) - ) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!( - "Alignment of ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).start) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(start) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).offset) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(offset) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3 { - pub segments: __le16, - pub dup_acks: __le16, -} -#[test] -fn bindgen_test_layout_virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 4usize, - concat!( - "Size of: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3) - ) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!( - "Alignment of ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).segments) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3), - "::", - stringify!(segments) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dup_acks) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3), - "::", - stringify!(dup_acks) - ) - ); -} -#[test] -fn bindgen_test_layout_virtio_net_hdr_v1__bindgen_ty_1() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 4usize, - concat!("Size of: ", stringify!(virtio_net_hdr_v1__bindgen_ty_1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!("Alignment of ", stringify!(virtio_net_hdr_v1__bindgen_ty_1)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).csum) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1), - "::", - stringify!(csum) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).rsc) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1__bindgen_ty_1), - "::", - stringify!(rsc) - ) - ); -} -impl Default for virtio_net_hdr_v1__bindgen_ty_1 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[test] -fn bindgen_test_layout_virtio_net_hdr_v1() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 12usize, - concat!("Size of: ", stringify!(virtio_net_hdr_v1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!("Alignment of ", stringify!(virtio_net_hdr_v1)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1), - "::", - stringify!(flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).gso_type) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1), - "::", - stringify!(gso_type) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).hdr_len) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1), - "::", - stringify!(hdr_len) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).gso_size) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1), - "::", - stringify!(gso_size) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).num_buffers) as usize - ptr as usize }, - 10usize, - concat!( - "Offset of field: ", - stringify!(virtio_net_hdr_v1), - "::", - stringify!(num_buffers) - ) - ); -} -impl Default for virtio_net_hdr_v1 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} diff --git a/src/vmm/src/devices/virtio/gen/mod.rs b/src/vmm/src/devices/virtio/generated/mod.rs similarity index 95% rename from src/vmm/src/devices/virtio/gen/mod.rs rename to src/vmm/src/devices/virtio/generated/mod.rs index b96b3e4efea..ab1a73dae7d 100644 --- a/src/vmm/src/devices/virtio/gen/mod.rs +++ b/src/vmm/src/devices/virtio/generated/mod.rs @@ -11,6 +11,6 @@ #![allow(non_snake_case)] pub mod virtio_blk; +pub mod virtio_config; pub mod virtio_net; pub mod virtio_ring; -pub mod virtio_rng; diff --git a/src/vmm/src/devices/virtio/gen/virtio_blk.rs b/src/vmm/src/devices/virtio/generated/virtio_blk.rs similarity index 51% rename from src/vmm/src/devices/virtio/gen/virtio_blk.rs rename to src/vmm/src/devices/virtio/generated/virtio_blk.rs index 7ef2b55ec21..624ef389ccd 100644 --- a/src/vmm/src/devices/virtio/gen/virtio_blk.rs +++ b/src/vmm/src/devices/virtio/generated/virtio_blk.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,17 +11,10 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] -pub const VIRTIO_F_NOTIFY_ON_EMPTY: u32 = 24; -pub const VIRTIO_F_ANY_LAYOUT: u32 = 27; -pub const VIRTIO_F_VERSION_1: u32 = 32; -pub const VIRTIO_F_ACCESS_PLATFORM: u32 = 33; -pub const VIRTIO_F_IOMMU_PLATFORM: u32 = 33; -pub const VIRTIO_F_RING_PACKED: u32 = 34; -pub const VIRTIO_F_ORDER_PLATFORM: u32 = 36; -pub const VIRTIO_F_SR_IOV: u32 = 37; pub const VIRTIO_BLK_F_SIZE_MAX: u32 = 1; pub const VIRTIO_BLK_F_SEG_MAX: u32 = 2; pub const VIRTIO_BLK_F_GEOMETRY: u32 = 4; @@ -31,6 +24,8 @@ pub const VIRTIO_BLK_F_TOPOLOGY: u32 = 10; pub const VIRTIO_BLK_F_MQ: u32 = 12; pub const VIRTIO_BLK_F_DISCARD: u32 = 13; pub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14; +pub const VIRTIO_BLK_F_SECURE_ERASE: u32 = 16; +pub const VIRTIO_BLK_F_ZONED: u32 = 17; pub const VIRTIO_BLK_F_BARRIER: u32 = 0; pub const VIRTIO_BLK_F_SCSI: u32 = 7; pub const VIRTIO_BLK_F_FLUSH: u32 = 9; @@ -44,8 +39,34 @@ pub const VIRTIO_BLK_T_FLUSH: u32 = 4; pub const VIRTIO_BLK_T_GET_ID: u32 = 8; pub const VIRTIO_BLK_T_DISCARD: u32 = 11; pub const VIRTIO_BLK_T_WRITE_ZEROES: u32 = 13; +pub const VIRTIO_BLK_T_SECURE_ERASE: u32 = 14; +pub const VIRTIO_BLK_T_ZONE_APPEND: u32 = 15; +pub const VIRTIO_BLK_T_ZONE_REPORT: u32 = 16; +pub const VIRTIO_BLK_T_ZONE_OPEN: u32 = 18; +pub const VIRTIO_BLK_T_ZONE_CLOSE: u32 = 20; +pub const VIRTIO_BLK_T_ZONE_FINISH: u32 = 22; +pub const VIRTIO_BLK_T_ZONE_RESET: u32 = 24; +pub const VIRTIO_BLK_T_ZONE_RESET_ALL: u32 = 26; pub const VIRTIO_BLK_T_BARRIER: u32 = 2147483648; +pub const VIRTIO_BLK_Z_NONE: u32 = 0; +pub const VIRTIO_BLK_Z_HM: u32 = 1; +pub const VIRTIO_BLK_Z_HA: u32 = 2; +pub const VIRTIO_BLK_ZT_CONV: u32 = 1; +pub const VIRTIO_BLK_ZT_SWR: u32 = 2; +pub const VIRTIO_BLK_ZT_SWP: u32 = 3; +pub const VIRTIO_BLK_ZS_NOT_WP: u32 = 0; +pub const VIRTIO_BLK_ZS_EMPTY: u32 = 1; +pub const VIRTIO_BLK_ZS_IOPEN: u32 = 2; +pub const VIRTIO_BLK_ZS_EOPEN: u32 = 3; +pub const VIRTIO_BLK_ZS_CLOSED: u32 = 4; +pub const VIRTIO_BLK_ZS_RDONLY: u32 = 13; +pub const VIRTIO_BLK_ZS_FULL: u32 = 14; +pub const VIRTIO_BLK_ZS_OFFLINE: u32 = 15; pub const VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP: u32 = 1; pub const VIRTIO_BLK_S_OK: u32 = 0; pub const VIRTIO_BLK_S_IOERR: u32 = 1; pub const VIRTIO_BLK_S_UNSUPP: u32 = 2; +pub const VIRTIO_BLK_S_ZONE_INVALID_CMD: u32 = 3; +pub const VIRTIO_BLK_S_ZONE_UNALIGNED_WP: u32 = 4; +pub const VIRTIO_BLK_S_ZONE_OPEN_RESOURCE: u32 = 5; +pub const VIRTIO_BLK_S_ZONE_ACTIVE_RESOURCE: u32 = 6; diff --git a/src/vmm/src/devices/virtio/gen/virtio_rng.rs b/src/vmm/src/devices/virtio/generated/virtio_config.rs similarity index 65% rename from src/vmm/src/devices/virtio/gen/virtio_rng.rs rename to src/vmm/src/devices/virtio/generated/virtio_config.rs index a0f43120693..e1ecc4fa38b 100644 --- a/src/vmm/src/devices/virtio/gen/virtio_rng.rs +++ b/src/vmm/src/devices/virtio/generated/virtio_config.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,7 +11,8 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] pub const VIRTIO_F_NOTIFY_ON_EMPTY: u32 = 24; @@ -20,5 +21,10 @@ pub const VIRTIO_F_VERSION_1: u32 = 32; pub const VIRTIO_F_ACCESS_PLATFORM: u32 = 33; pub const VIRTIO_F_IOMMU_PLATFORM: u32 = 33; pub const VIRTIO_F_RING_PACKED: u32 = 34; +pub const VIRTIO_F_IN_ORDER: u32 = 35; pub const VIRTIO_F_ORDER_PLATFORM: u32 = 36; pub const VIRTIO_F_SR_IOV: u32 = 37; +pub const VIRTIO_F_NOTIFICATION_DATA: u32 = 38; +pub const VIRTIO_F_NOTIF_CONFIG_DATA: u32 = 39; +pub const VIRTIO_F_RING_RESET: u32 = 40; +pub const VIRTIO_F_ADMIN_VQ: u32 = 41; diff --git a/src/vmm/src/devices/virtio/generated/virtio_net.rs b/src/vmm/src/devices/virtio/generated/virtio_net.rs new file mode 100644 index 00000000000..6d5adaac8f6 --- /dev/null +++ b/src/vmm/src/devices/virtio/generated/virtio_net.rs @@ -0,0 +1,171 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// automatically generated by tools/bindgen.sh + +#![allow( + non_camel_case_types, + non_upper_case_globals, + dead_code, + non_snake_case, + clippy::ptr_as_ptr, + clippy::undocumented_unsafe_blocks, + missing_debug_implementations, + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn +)] + +pub const VIRTIO_NET_F_CSUM: u32 = 0; +pub const VIRTIO_NET_F_GUEST_CSUM: u32 = 1; +pub const VIRTIO_NET_F_CTRL_GUEST_OFFLOADS: u32 = 2; +pub const VIRTIO_NET_F_MTU: u32 = 3; +pub const VIRTIO_NET_F_MAC: u32 = 5; +pub const VIRTIO_NET_F_GUEST_TSO4: u32 = 7; +pub const VIRTIO_NET_F_GUEST_TSO6: u32 = 8; +pub const VIRTIO_NET_F_GUEST_ECN: u32 = 9; +pub const VIRTIO_NET_F_GUEST_UFO: u32 = 10; +pub const VIRTIO_NET_F_HOST_TSO4: u32 = 11; +pub const VIRTIO_NET_F_HOST_TSO6: u32 = 12; +pub const VIRTIO_NET_F_HOST_ECN: u32 = 13; +pub const VIRTIO_NET_F_HOST_UFO: u32 = 14; +pub const VIRTIO_NET_F_MRG_RXBUF: u32 = 15; +pub const VIRTIO_NET_F_STATUS: u32 = 16; +pub const VIRTIO_NET_F_CTRL_VQ: u32 = 17; +pub const VIRTIO_NET_F_CTRL_RX: u32 = 18; +pub const VIRTIO_NET_F_CTRL_VLAN: u32 = 19; +pub const VIRTIO_NET_F_CTRL_RX_EXTRA: u32 = 20; +pub const VIRTIO_NET_F_GUEST_ANNOUNCE: u32 = 21; +pub const VIRTIO_NET_F_MQ: u32 = 22; +pub const VIRTIO_NET_F_CTRL_MAC_ADDR: u32 = 23; +pub const VIRTIO_NET_F_VQ_NOTF_COAL: u32 = 52; +pub const VIRTIO_NET_F_NOTF_COAL: u32 = 53; +pub const VIRTIO_NET_F_GUEST_USO4: u32 = 54; +pub const VIRTIO_NET_F_GUEST_USO6: u32 = 55; +pub const VIRTIO_NET_F_HOST_USO: u32 = 56; +pub const VIRTIO_NET_F_HASH_REPORT: u32 = 57; +pub const VIRTIO_NET_F_GUEST_HDRLEN: u32 = 59; +pub const VIRTIO_NET_F_RSS: u32 = 60; +pub const VIRTIO_NET_F_RSC_EXT: u32 = 61; +pub const VIRTIO_NET_F_STANDBY: u32 = 62; +pub const VIRTIO_NET_F_SPEED_DUPLEX: u32 = 63; +pub const VIRTIO_NET_F_GSO: u32 = 6; +pub type __u8 = ::std::os::raw::c_uchar; +pub type __u16 = ::std::os::raw::c_ushort; +pub type __le16 = __u16; +pub type __virtio16 = __u16; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct virtio_net_hdr_v1 { + pub flags: __u8, + pub gso_type: __u8, + pub hdr_len: __virtio16, + pub gso_size: __virtio16, + pub __bindgen_anon_1: virtio_net_hdr_v1__bindgen_ty_1, + pub num_buffers: __virtio16, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union virtio_net_hdr_v1__bindgen_ty_1 { + pub __bindgen_anon_1: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1, + pub csum: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2, + pub rsc: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3, +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1 { + pub csum_start: __virtio16, + pub csum_offset: __virtio16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1"] + [::std::mem::size_of::() - 4usize]; + ["Alignment of virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1"] + [::std::mem::align_of::() - 2usize]; + ["Offset of field: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1::csum_start"][::std::mem::offset_of!( + virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1, + csum_start + ) - 0usize]; + ["Offset of field: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1::csum_offset"][::std::mem::offset_of!( + virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_1, + csum_offset + ) - 2usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2 { + pub start: __virtio16, + pub offset: __virtio16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2"] + [::std::mem::size_of::() - 4usize]; + ["Alignment of virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2"] + [::std::mem::align_of::() - 2usize]; + ["Offset of field: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2::start"] + [::std::mem::offset_of!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2, start) - 0usize]; + ["Offset of field: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2::offset"] + [::std::mem::offset_of!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_2, offset) - 2usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3 { + pub segments: __le16, + pub dup_acks: __le16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3"] + [::std::mem::size_of::() - 4usize]; + ["Alignment of virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3"] + [::std::mem::align_of::() - 2usize]; + ["Offset of field: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3::segments"] + [::std::mem::offset_of!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3, segments) - 0usize]; + ["Offset of field: virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3::dup_acks"] + [::std::mem::offset_of!(virtio_net_hdr_v1__bindgen_ty_1__bindgen_ty_3, dup_acks) - 2usize]; +}; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of virtio_net_hdr_v1__bindgen_ty_1"] + [::std::mem::size_of::() - 4usize]; + ["Alignment of virtio_net_hdr_v1__bindgen_ty_1"] + [::std::mem::align_of::() - 2usize]; + ["Offset of field: virtio_net_hdr_v1__bindgen_ty_1::csum"] + [::std::mem::offset_of!(virtio_net_hdr_v1__bindgen_ty_1, csum) - 0usize]; + ["Offset of field: virtio_net_hdr_v1__bindgen_ty_1::rsc"] + [::std::mem::offset_of!(virtio_net_hdr_v1__bindgen_ty_1, rsc) - 0usize]; +}; +impl Default for virtio_net_hdr_v1__bindgen_ty_1 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of virtio_net_hdr_v1"][::std::mem::size_of::() - 12usize]; + ["Alignment of virtio_net_hdr_v1"][::std::mem::align_of::() - 2usize]; + ["Offset of field: virtio_net_hdr_v1::flags"] + [::std::mem::offset_of!(virtio_net_hdr_v1, flags) - 0usize]; + ["Offset of field: virtio_net_hdr_v1::gso_type"] + [::std::mem::offset_of!(virtio_net_hdr_v1, gso_type) - 1usize]; + ["Offset of field: virtio_net_hdr_v1::hdr_len"] + [::std::mem::offset_of!(virtio_net_hdr_v1, hdr_len) - 2usize]; + ["Offset of field: virtio_net_hdr_v1::gso_size"] + [::std::mem::offset_of!(virtio_net_hdr_v1, gso_size) - 4usize]; + ["Offset of field: virtio_net_hdr_v1::num_buffers"] + [::std::mem::offset_of!(virtio_net_hdr_v1, num_buffers) - 10usize]; +}; +impl Default for virtio_net_hdr_v1 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} diff --git a/src/vmm/src/devices/virtio/gen/virtio_ring.rs b/src/vmm/src/devices/virtio/generated/virtio_ring.rs similarity index 72% rename from src/vmm/src/devices/virtio/gen/virtio_ring.rs rename to src/vmm/src/devices/virtio/generated/virtio_ring.rs index 9d228ea9ee8..a0411fcc92b 100644 --- a/src/vmm/src/devices/virtio/gen/virtio_ring.rs +++ b/src/vmm/src/devices/virtio/generated/virtio_ring.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,7 +11,8 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] pub const VIRTIO_RING_F_EVENT_IDX: u32 = 29; diff --git a/src/vmm/src/devices/virtio/iov_deque.rs b/src/vmm/src/devices/virtio/iov_deque.rs index 51bf28a49eb..55587d83843 100644 --- a/src/vmm/src/devices/virtio/iov_deque.rs +++ b/src/vmm/src/devices/virtio/iov_deque.rs @@ -6,7 +6,7 @@ use std::os::fd::AsRawFd; use libc::{c_int, c_void, iovec, off_t, size_t}; use memfd; -use crate::arch::PAGE_SIZE; +use crate::arch::host_page_size; #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum IovDequeError { @@ -77,10 +77,7 @@ pub enum IovDequeError { // pub iov_len: ::size_t, // } // ``` -// -// This value must be a multiple of 256 because this is the maximum number of `iovec` can fit into -// 1 memory page: 256 * sizeof(iovec) == 4096 == PAGE_SIZE. IovDeque only operates with `PAGE_SIZE` -// granularity. + #[derive(Debug)] pub struct IovDeque { pub iov: *mut libc::iovec, @@ -92,18 +89,15 @@ pub struct IovDeque { unsafe impl Send for IovDeque {} impl IovDeque { - const BYTES: usize = L as usize * std::mem::size_of::(); - const _ASSERT: () = assert!(Self::BYTES % PAGE_SIZE == 0); - /// Create a [`memfd`] object that represents a single physical page - fn create_memfd() -> Result { + fn create_memfd(pages_bytes: usize) -> Result { // Create a sealable memfd. let opts = memfd::MemfdOptions::default().allow_sealing(true); let mfd = opts.create("iov_deque")?; // Resize to system page size. mfd.as_file() - .set_len(Self::BYTES.try_into().unwrap()) + .set_len(pages_bytes.try_into().unwrap()) .map_err(IovDequeError::MemfdResize)?; // Add seals to prevent further resizing. @@ -126,7 +120,8 @@ impl IovDeque { fd: c_int, offset: off_t, ) -> Result<*mut c_void, IovDequeError> { - let ptr = libc::mmap(addr, len, prot, flags, fd, offset); + // SAFETY: caller should ensure the parameters are valid + let ptr = unsafe { libc::mmap(addr, len, prot, flags, fd, offset) }; if ptr == libc::MAP_FAILED { return Err(IovDequeError::Mmap(std::io::Error::last_os_error())); } @@ -136,13 +131,13 @@ impl IovDeque { /// Allocate memory for our ring buffer /// - /// This will allocate 2 * `Self::BYTES` bytes of virtual memory. - fn allocate_ring_buffer_memory() -> Result<*mut c_void, IovDequeError> { + /// This will allocate 2 * `pages_bytes` bytes of virtual memory. + fn allocate_ring_buffer_memory(pages_bytes: usize) -> Result<*mut c_void, IovDequeError> { // SAFETY: We are calling the system call with valid arguments unsafe { Self::mmap( std::ptr::null_mut(), - Self::BYTES * 2, + pages_bytes * 2, libc::PROT_NONE, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, @@ -151,18 +146,29 @@ impl IovDeque { } } + /// Calculate a number of bytes in full pages required for + /// the type to operate. + fn pages_bytes() -> usize { + let host_page_size = host_page_size(); + let bytes = L as usize * std::mem::size_of::(); + let num_host_pages = bytes.div_ceil(host_page_size); + num_host_pages * host_page_size + } + /// Create a new [`IovDeque`] that can hold memory described by a single VirtIO queue. pub fn new() -> Result { - let memfd = Self::create_memfd()?; + let pages_bytes = Self::pages_bytes(); + + let memfd = Self::create_memfd(pages_bytes)?; let raw_memfd = memfd.as_file().as_raw_fd(); - let buffer = Self::allocate_ring_buffer_memory()?; + let buffer = Self::allocate_ring_buffer_memory(pages_bytes)?; // Map the first page of virtual memory to the physical page described by the memfd object // SAFETY: We are calling the system call with valid arguments let _ = unsafe { Self::mmap( buffer, - Self::BYTES, + pages_bytes, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED | libc::MAP_FIXED, raw_memfd, @@ -173,17 +179,17 @@ impl IovDeque { // Map the second page of virtual memory to the physical page described by the memfd object // // SAFETY: This is safe because: - // * Both `buffer` and the result of `buffer.add(Self::BYTES)` are within bounds of the + // * Both `buffer` and the result of `buffer.add(pages_bytes)` are within bounds of the // allocation we got from `Self::allocate_ring_buffer_memory`. // * The resulting pointer is the beginning of the second page of our allocation, so it // doesn't wrap around the address space. - let next_page = unsafe { buffer.add(Self::BYTES) }; + let next_page = unsafe { buffer.add(pages_bytes) }; // SAFETY: We are calling the system call with valid arguments let _ = unsafe { Self::mmap( next_page, - Self::BYTES, + pages_bytes, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED | libc::MAP_FIXED, raw_memfd, @@ -311,9 +317,10 @@ impl IovDeque { impl Drop for IovDeque { fn drop(&mut self) { + let pages_bytes = Self::pages_bytes(); // SAFETY: We are passing an address that we got from a previous allocation of `2 * - // Self::BYTES` bytes by calling mmap - let _ = unsafe { libc::munmap(self.iov.cast(), Self::BYTES * 2) }; + // pages_bytes` by calling mmap + let _ = unsafe { libc::munmap(self.iov.cast(), 2 * pages_bytes) }; } } @@ -331,6 +338,18 @@ mod tests { assert_eq!(deque.len(), 0); } + #[test] + fn test_new_less_than_page() { + let deque = super::IovDeque::<128>::new().unwrap(); + assert_eq!(deque.len(), 0); + } + + #[test] + fn test_new_more_than_page() { + let deque = super::IovDeque::<512>::new().unwrap(); + assert_eq!(deque.len(), 0); + } + fn make_iovec(id: u16, len: u16) -> iovec { iovec { iov_base: id as *mut libc::c_void, diff --git a/src/vmm/src/devices/virtio/iovec.rs b/src/vmm/src/devices/virtio/iovec.rs index 4cbbc512186..3865cc7ecf2 100644 --- a/src/vmm/src/devices/virtio/iovec.rs +++ b/src/vmm/src/devices/virtio/iovec.rs @@ -100,7 +100,10 @@ impl IoVecBuffer { head: DescriptorChain, ) -> Result { let mut new_buffer = Self::default(); - new_buffer.load_descriptor_chain(mem, head)?; + // SAFETY: descriptor chain cannot be referencing the same memory location as another chain + unsafe { + new_buffer.load_descriptor_chain(mem, head)?; + } Ok(new_buffer) } @@ -194,7 +197,7 @@ impl IoVecBuffer { Err(VolatileMemoryError::IOError(err)) if err.kind() == ErrorKind::Interrupted => { - continue + continue; } Ok(bytes_read) => break bytes_read, Err(volatile_memory_error) => return Err(volatile_memory_error), @@ -264,10 +267,11 @@ impl IoVecBufferMut { // We use get_slice instead of `get_host_address` here in order to have the whole // range of the descriptor chain checked, i.e. [addr, addr + len) is a valid memory // region in the GuestMemoryMmap. - let slice = mem.get_slice(desc.addr, desc.len as usize).map_err(|err| { - self.vecs.pop_back(nr_iovecs); - err - })?; + let slice = mem + .get_slice(desc.addr, desc.len as usize) + .inspect_err(|_| { + self.vecs.pop_back(nr_iovecs); + })?; // We need to mark the area of guest memory that will be mutated through this // IoVecBufferMut as dirty ahead of time, as we loose access to all // vm-memory related information after converting down to iovecs. @@ -288,9 +292,8 @@ impl IoVecBufferMut { length = length .checked_add(desc.len) .ok_or(IoVecError::OverflowedDescriptor) - .map_err(|err| { + .inspect_err(|_| { self.vecs.pop_back(nr_iovecs); - err })?; next_descriptor = desc.next_descriptor(); @@ -328,7 +331,8 @@ impl IoVecBufferMut { head: DescriptorChain, ) -> Result<(), IoVecError> { self.clear(); - let _ = self.append_descriptor_chain(mem, head)?; + // SAFETY: descriptor chain cannot be referencing the same memory location as another chain + let _ = unsafe { self.append_descriptor_chain(mem, head)? }; Ok(()) } @@ -358,7 +362,10 @@ impl IoVecBufferMut { head: DescriptorChain, ) -> Result { let mut new_buffer = Self::new()?; - new_buffer.load_descriptor_chain(mem, head)?; + // SAFETY: descriptor chain cannot be referencing the same memory location as another chain + unsafe { + new_buffer.load_descriptor_chain(mem, head)?; + } Ok(new_buffer) } @@ -455,7 +462,7 @@ impl IoVecBufferMut { Err(VolatileMemoryError::IOError(err)) if err.kind() == ErrorKind::Interrupted => { - continue + continue; } Ok(bytes_read) => break bytes_read, Err(volatile_memory_error) => return Err(volatile_memory_error), @@ -482,10 +489,12 @@ mod tests { use super::IoVecBuffer; // Redefine `IoVecBufferMut` with specific length. Otherwise // Rust will not know what to do. - type IoVecBufferMut = super::IoVecBufferMut<256>; + type IoVecBufferMutDefault = super::IoVecBufferMut; use crate::devices::virtio::iov_deque::IovDeque; - use crate::devices::virtio::queue::{Queue, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE}; + use crate::devices::virtio::queue::{ + FIRECRACKER_MAX_QUEUE_SIZE, Queue, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE, + }; use crate::devices::virtio::test_utils::VirtQueue; use crate::test_utils::multi_region_mem; use crate::vstate::memory::{Bytes, GuestAddress, GuestMemoryMmap}; @@ -614,12 +623,12 @@ mod tests { let (mut q, _) = read_only_chain(&mem); let head = q.pop().unwrap(); // SAFETY: This descriptor chain is only loaded into one buffer - unsafe { IoVecBufferMut::from_descriptor_chain(&mem, head).unwrap_err() }; + unsafe { IoVecBufferMutDefault::from_descriptor_chain(&mem, head).unwrap_err() }; let (mut q, _) = write_only_chain(&mem); let head = q.pop().unwrap(); // SAFETY: This descriptor chain is only loaded into one buffer - unsafe { IoVecBufferMut::from_descriptor_chain(&mem, head).unwrap() }; + unsafe { IoVecBufferMutDefault::from_descriptor_chain(&mem, head).unwrap() }; } #[test] @@ -640,7 +649,8 @@ mod tests { let head = q.pop().unwrap(); // SAFETY: This descriptor chain is only loaded once in this test - let mut iovec = unsafe { IoVecBufferMut::from_descriptor_chain(&mem, head).unwrap() }; + let mut iovec = + unsafe { IoVecBufferMutDefault::from_descriptor_chain(&mem, head).unwrap() }; assert_eq!(iovec.len(), 4 * 64); // We are creating a new queue where we can get descriptors from. Probably, this is not @@ -717,7 +727,8 @@ mod tests { let head = q.pop().unwrap(); // SAFETY: This descriptor chain is only loaded into one buffer - let mut iovec = unsafe { IoVecBufferMut::from_descriptor_chain(&mem, head).unwrap() }; + let mut iovec = + unsafe { IoVecBufferMutDefault::from_descriptor_chain(&mem, head).unwrap() }; let buf = vec![0u8, 1, 2, 3, 4]; // One test vector for each part of the chain @@ -807,17 +818,17 @@ mod verification { use std::mem::ManuallyDrop; use libc::{c_void, iovec}; - use vm_memory::bitmap::BitmapSlice; use vm_memory::VolatileSlice; + use vm_memory::bitmap::BitmapSlice; use super::IoVecBuffer; + use crate::arch::GUEST_PAGE_SIZE; use crate::devices::virtio::iov_deque::IovDeque; // Redefine `IoVecBufferMut` and `IovDeque` with specific length. Otherwise // Rust will not know what to do. - type IoVecBufferMut256 = super::IoVecBufferMut<256>; - type IovDeque256 = IovDeque<256>; + type IoVecBufferMutDefault = super::IoVecBufferMut; + type IovDequeDefault = IovDeque; - use crate::arch::PAGE_SIZE; use crate::devices::virtio::queue::FIRECRACKER_MAX_QUEUE_SIZE; // Maximum memory size to use for our buffers. For the time being 1KB. @@ -860,10 +871,10 @@ mod verification { ); let offset = (deque.start + deque.len) as usize; - let mirror = if offset >= FIRECRACKER_MAX_QUEUE_SIZE as usize { - offset - FIRECRACKER_MAX_QUEUE_SIZE as usize + let mirror = if offset >= L as usize { + offset - L as usize } else { - offset + FIRECRACKER_MAX_QUEUE_SIZE as usize + offset + L as usize }; // SAFETY: self.iov is a valid pointer and `self.start + self.len` is within range (we @@ -904,22 +915,22 @@ mod verification { } } - fn create_iov_deque() -> IovDeque256 { + fn create_iov_deque() -> IovDequeDefault { // SAFETY: safe because the layout has non-zero size let mem = unsafe { std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked( - 2 * PAGE_SIZE, - PAGE_SIZE, + 2 * GUEST_PAGE_SIZE, + GUEST_PAGE_SIZE, )) }; - IovDeque256 { + IovDequeDefault { iov: mem.cast(), start: kani::any_where(|&start| start < FIRECRACKER_MAX_QUEUE_SIZE), len: 0, } } - fn create_iovecs_mut(mem: *mut u8, size: usize, nr_descs: usize) -> (IovDeque256, u32) { + fn create_iovecs_mut(mem: *mut u8, size: usize, nr_descs: usize) -> (IovDequeDefault, u32) { let mut vecs = create_iov_deque(); let mut len = 0u32; for _ in 0..nr_descs { @@ -939,7 +950,7 @@ mod verification { (vecs, len) } - impl IoVecBufferMut256 { + impl IoVecBufferMutDefault { fn any_of_length(nr_descs: usize) -> Self { // We only write into `IoVecBufferMut` objects, so we can simply create a guest memory // object initialized to zeroes, trying to be nice to Kani. @@ -1029,12 +1040,10 @@ mod verification { #[kani::proof] #[kani::unwind(5)] #[kani::solver(cadical)] - // The `IovDeque` is defined as type alias in the kani module. Because of this - // we need to specify original type here for stub to work. #[kani::stub(IovDeque::push_back, stubs::push_back)] fn verify_write_to_iovec() { for nr_descs in 0..MAX_DESC_LENGTH { - let mut iov_mut = IoVecBufferMut256::any_of_length(nr_descs); + let mut iov_mut = IoVecBufferMutDefault::any_of_length(nr_descs); let mut buf = kani::vec::any_vec::(); let offset: u32 = kani::any(); diff --git a/src/vmm/src/devices/virtio/mmio.rs b/src/vmm/src/devices/virtio/mmio.rs index 463d11ca2e2..12ee54bfb0a 100644 --- a/src/vmm/src/devices/virtio/mmio.rs +++ b/src/vmm/src/devices/virtio/mmio.rs @@ -38,9 +38,9 @@ const MMIO_VERSION: u32 = 2; /// /// 1. Mmio reads and writes must be sent to this device at what is referred to here as MMIO base. /// 1. `Mmio::queue_evts` must be installed at `virtio::NOTIFY_REG_OFFSET` offset from the MMIO -/// base. Each event in the array must be signaled if the index is written at that offset. +/// base. Each event in the array must be signaled if the index is written at that offset. /// 1. `Mmio::interrupt_evt` must signal an interrupt that the guest driver is listening to when it -/// is written to. +/// is written to. /// /// Typically one page (4096 bytes) of MMIO address space is sufficient to handle this transport /// and inner virtio device. @@ -138,7 +138,7 @@ impl MmioTransport { self.with_queue_mut(f); } else { warn!( - "update virtio queue in invalid state 0x{:x}", + "update virtio queue in invalid state {:#x}", self.device_status ); } @@ -227,7 +227,7 @@ impl MmioTransport { } _ => { warn!( - "invalid virtio driver status transition: 0x{:x} -> 0x{:x}", + "invalid virtio driver status transition: {:#x} -> {:#x}", self.device_status, status ); } @@ -282,7 +282,7 @@ impl MmioTransport { 0x70 => self.device_status, 0xfc => self.config_generation, _ => { - warn!("unknown virtio mmio register read: 0x{:x}", offset); + warn!("unknown virtio mmio register read: {:#x}", offset); return; } }; @@ -290,11 +290,7 @@ impl MmioTransport { } 0x100..=0xfff => self.locked_device().read_config(offset - 0x100, data), _ => { - warn!( - "invalid virtio mmio read: 0x{:x}:0x{:x}", - offset, - data.len() - ); + warn!("invalid virtio mmio read: {:#x}:{:#x}", offset, data.len()); } }; } @@ -324,7 +320,7 @@ impl MmioTransport { .ack_features_by_page(self.acked_features_select, v); } else { warn!( - "ack virtio features in invalid state 0x{:x}", + "ack virtio features in invalid state {:#x}", self.device_status ); } @@ -346,7 +342,7 @@ impl MmioTransport { 0xa0 => self.update_queue_field(|q| lo(&mut q.used_ring_address, v)), 0xa4 => self.update_queue_field(|q| hi(&mut q.used_ring_address, v)), _ => { - warn!("unknown virtio mmio register write: 0x{:x}", offset); + warn!("unknown virtio mmio register write: {:#x}", offset); } } } @@ -361,11 +357,7 @@ impl MmioTransport { } } _ => { - warn!( - "invalid virtio mmio write: 0x{:x}:0x{:x}", - offset, - data.len() - ); + warn!("invalid virtio mmio write: {:#x}:{:#x}", offset, data.len()); } } } @@ -376,9 +368,9 @@ pub(crate) mod tests { use vmm_sys_util::eventfd::EventFd; use super::*; + use crate::devices::virtio::ActivateError; use crate::devices::virtio::device::IrqTrigger; use crate::devices::virtio::device_status::DEVICE_NEEDS_RESET; - use crate::devices::virtio::ActivateError; use crate::test_utils::single_region_mem; use crate::utils::byte_order::{read_le_u32, write_le_u32}; use crate::utils::u64_to_usize; diff --git a/src/vmm/src/devices/virtio/mod.rs b/src/vmm/src/devices/virtio/mod.rs index 9931e1211d1..f298d28e9bd 100644 --- a/src/vmm/src/devices/virtio/mod.rs +++ b/src/vmm/src/devices/virtio/mod.rs @@ -15,7 +15,7 @@ use crate::devices::virtio::net::TapError; pub mod balloon; pub mod block; pub mod device; -pub mod gen; +pub mod generated; mod iov_deque; pub mod iovec; pub mod mmio; diff --git a/src/vmm/src/devices/virtio/net/device.rs b/src/vmm/src/devices/virtio/net/device.rs index 6c69bd171a7..fff04d1da1a 100755 --- a/src/vmm/src/devices/virtio/net/device.rs +++ b/src/vmm/src/devices/virtio/net/device.rs @@ -10,30 +10,30 @@ use std::mem::{self}; use std::net::Ipv4Addr; use std::sync::{Arc, Mutex}; -use libc::{iovec, EAGAIN}; +use libc::{EAGAIN, iovec}; use log::error; use vmm_sys_util::eventfd::EventFd; use super::NET_QUEUE_MAX_SIZE; use crate::devices::virtio::device::{DeviceState, IrqTrigger, IrqType, VirtioDevice}; -use crate::devices::virtio::gen::virtio_blk::VIRTIO_F_VERSION_1; -use crate::devices::virtio::gen::virtio_net::{ - virtio_net_hdr_v1, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, - VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MRG_RXBUF, +use crate::devices::virtio::generated::virtio_config::VIRTIO_F_VERSION_1; +use crate::devices::virtio::generated::virtio_net::{ + VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MAC, VIRTIO_NET_F_MRG_RXBUF, virtio_net_hdr_v1, }; -use crate::devices::virtio::gen::virtio_ring::VIRTIO_RING_F_EVENT_IDX; +use crate::devices::virtio::generated::virtio_ring::VIRTIO_RING_F_EVENT_IDX; use crate::devices::virtio::iovec::{ IoVecBuffer, IoVecBufferMut, IoVecError, ParsedDescriptorChain, }; use crate::devices::virtio::net::metrics::{NetDeviceMetrics, NetMetricsPerDevice}; use crate::devices::virtio::net::tap::Tap; use crate::devices::virtio::net::{ - gen, NetError, NetQueue, MAX_BUFFER_SIZE, NET_QUEUE_SIZES, RX_INDEX, TX_INDEX, + MAX_BUFFER_SIZE, NET_QUEUE_SIZES, NetError, NetQueue, RX_INDEX, TX_INDEX, generated, }; -use crate::devices::virtio::queue::{DescriptorChain, Queue, FIRECRACKER_MAX_QUEUE_SIZE}; +use crate::devices::virtio::queue::{DescriptorChain, Queue}; use crate::devices::virtio::{ActivateError, TYPE_NET}; -use crate::devices::{report_net_event_fail, DeviceError}; +use crate::devices::{DeviceError, report_net_event_fail}; use crate::dumbo::pdu::arp::ETH_IPV4_FRAME_LEN; use crate::dumbo::pdu::ethernet::{EthernetFrame, PAYLOAD_OFFSET}; use crate::logger::{IncMetric, METRICS}; @@ -119,7 +119,7 @@ impl RxBuffers { Ok(Self { min_buffer_size: 0, iovec: IoVecBufferMut::new()?, - parsed_descriptors: VecDeque::with_capacity(FIRECRACKER_MAX_QUEUE_SIZE.into()), + parsed_descriptors: VecDeque::with_capacity(NET_QUEUE_MAX_SIZE.into()), used_descriptors: 0, used_bytes: 0, }) @@ -135,7 +135,8 @@ impl RxBuffers { mem: &GuestMemoryMmap, head: DescriptorChain, ) -> Result<(), AddRxBufferError> { - let parsed_dc = self.iovec.append_descriptor_chain(mem, head)?; + // SAFETY: descriptor chain cannot be referencing the same memory location as another chain + let parsed_dc = unsafe { self.iovec.append_descriptor_chain(mem, head)? }; if parsed_dc.length < self.min_buffer_size { self.iovec.drop_chain_back(&parsed_dc); return Err(AddRxBufferError::BufferTooSmall); @@ -274,17 +275,17 @@ impl Net { rx_rate_limiter: RateLimiter, tx_rate_limiter: RateLimiter, ) -> Result { - let mut avail_features = 1 << VIRTIO_NET_F_GUEST_CSUM - | 1 << VIRTIO_NET_F_CSUM - | 1 << VIRTIO_NET_F_GUEST_TSO4 - | 1 << VIRTIO_NET_F_GUEST_TSO6 - | 1 << VIRTIO_NET_F_GUEST_UFO - | 1 << VIRTIO_NET_F_HOST_TSO4 - | 1 << VIRTIO_NET_F_HOST_TSO6 - | 1 << VIRTIO_NET_F_HOST_UFO - | 1 << VIRTIO_F_VERSION_1 - | 1 << VIRTIO_NET_F_MRG_RXBUF - | 1 << VIRTIO_RING_F_EVENT_IDX; + let mut avail_features = (1 << VIRTIO_NET_F_GUEST_CSUM) + | (1 << VIRTIO_NET_F_CSUM) + | (1 << VIRTIO_NET_F_GUEST_TSO4) + | (1 << VIRTIO_NET_F_GUEST_TSO6) + | (1 << VIRTIO_NET_F_GUEST_UFO) + | (1 << VIRTIO_NET_F_HOST_TSO4) + | (1 << VIRTIO_NET_F_HOST_TSO6) + | (1 << VIRTIO_NET_F_HOST_UFO) + | (1 << VIRTIO_F_VERSION_1) + | (1 << VIRTIO_NET_F_MRG_RXBUF) + | (1 << VIRTIO_RING_F_EVENT_IDX); let mut config_space = ConfigSpace::default(); if let Some(mac) = guest_mac { @@ -514,10 +515,9 @@ impl Net { NetError::VnetHeaderMissing })?; - let headers = frame_bytes_from_buf(&headers[..header_len]).map_err(|e| { + let headers = frame_bytes_from_buf(&headers[..header_len]).inspect_err(|_| { error!("VNET headers missing in TX frame"); net_metrics.tx_malformed_frames.inc(); - e })?; if let Some(ns) = mmds_ns { @@ -778,25 +778,25 @@ impl Net { add_if_supported( &mut tap_features, guest_supported_features, - gen::TUN_F_CSUM, + generated::TUN_F_CSUM, VIRTIO_NET_F_GUEST_CSUM, ); add_if_supported( &mut tap_features, guest_supported_features, - gen::TUN_F_UFO, + generated::TUN_F_UFO, VIRTIO_NET_F_GUEST_UFO, ); add_if_supported( &mut tap_features, guest_supported_features, - gen::TUN_F_TSO4, + generated::TUN_F_TSO4, VIRTIO_NET_F_GUEST_TSO4, ); add_if_supported( &mut tap_features, guest_supported_features, - gen::TUN_F_TSO6, + generated::TUN_F_TSO6, VIRTIO_NET_F_GUEST_TSO6, ); @@ -1040,26 +1040,26 @@ pub mod tests { use super::*; use crate::check_metric_after_block; - use crate::devices::virtio::gen::virtio_ring::VIRTIO_RING_F_EVENT_IDX; + use crate::devices::virtio::generated::virtio_ring::VIRTIO_RING_F_EVENT_IDX; use crate::devices::virtio::iovec::IoVecBuffer; + use crate::devices::virtio::net::NET_QUEUE_SIZES; use crate::devices::virtio::net::device::{ frame_bytes_from_buf, frame_bytes_from_buf_mut, frame_hdr_len, init_vnet_hdr, vnet_hdr_len, }; use crate::devices::virtio::net::test_utils::test::TestHelper; use crate::devices::virtio::net::test_utils::{ - default_net, if_index, inject_tap_tx_frame, set_mac, NetEvent, NetQueue, - TapTrafficSimulator, + NetEvent, NetQueue, TapTrafficSimulator, default_net, if_index, inject_tap_tx_frame, + set_mac, }; - use crate::devices::virtio::net::NET_QUEUE_SIZES; use crate::devices::virtio::queue::VIRTQ_DESC_F_WRITE; use crate::devices::virtio::test_utils::VirtQueue; - use crate::dumbo::pdu::arp::{EthIPv4ArpFrame, ETH_IPV4_FRAME_LEN}; - use crate::dumbo::pdu::ethernet::ETHERTYPE_ARP; use crate::dumbo::EthernetFrame; + use crate::dumbo::pdu::arp::{ETH_IPV4_FRAME_LEN, EthIPv4ArpFrame}; + use crate::dumbo::pdu::ethernet::ETHERTYPE_ARP; use crate::logger::IncMetric; use crate::rate_limiter::{BucketUpdate, RateLimiter, TokenBucket, TokenType}; use crate::test_utils::single_region_mem; - use crate::utils::net::mac::{MacAddr, MAC_ADDR_LEN}; + use crate::utils::net::mac::{MAC_ADDR_LEN, MacAddr}; use crate::vstate::memory::{Address, GuestMemory}; impl Net { @@ -1116,18 +1116,18 @@ pub mod tests { set_mac(&mut net, MacAddr::from_str("11:22:33:44:55:66").unwrap()); // Test `features()` and `ack_features()`. - let features = 1 << VIRTIO_NET_F_GUEST_CSUM - | 1 << VIRTIO_NET_F_CSUM - | 1 << VIRTIO_NET_F_GUEST_TSO4 - | 1 << VIRTIO_NET_F_GUEST_TSO6 - | 1 << VIRTIO_NET_F_MAC - | 1 << VIRTIO_NET_F_GUEST_UFO - | 1 << VIRTIO_NET_F_HOST_TSO4 - | 1 << VIRTIO_NET_F_HOST_TSO6 - | 1 << VIRTIO_NET_F_HOST_UFO - | 1 << VIRTIO_F_VERSION_1 - | 1 << VIRTIO_NET_F_MRG_RXBUF - | 1 << VIRTIO_RING_F_EVENT_IDX; + let features = (1 << VIRTIO_NET_F_GUEST_CSUM) + | (1 << VIRTIO_NET_F_CSUM) + | (1 << VIRTIO_NET_F_GUEST_TSO4) + | (1 << VIRTIO_NET_F_GUEST_TSO6) + | (1 << VIRTIO_NET_F_MAC) + | (1 << VIRTIO_NET_F_GUEST_UFO) + | (1 << VIRTIO_NET_F_HOST_TSO4) + | (1 << VIRTIO_NET_F_HOST_TSO6) + | (1 << VIRTIO_NET_F_HOST_UFO) + | (1 << VIRTIO_F_VERSION_1) + | (1 << VIRTIO_NET_F_MRG_RXBUF) + | (1 << VIRTIO_RING_F_EVENT_IDX); assert_eq!( net.avail_features_by_page(0), @@ -1149,12 +1149,14 @@ pub mod tests { // Test that `Net::build_tap_offload_features` creates the TAP offload features that we expect // it to do, based on the available guest features fn test_build_tap_offload_features_all() { - let supported_features = 1 << VIRTIO_NET_F_GUEST_CSUM - | 1 << VIRTIO_NET_F_GUEST_UFO - | 1 << VIRTIO_NET_F_GUEST_TSO4 - | 1 << VIRTIO_NET_F_GUEST_TSO6; - let expected_tap_features = - gen::TUN_F_CSUM | gen::TUN_F_UFO | gen::TUN_F_TSO4 | gen::TUN_F_TSO6; + let supported_features = (1 << VIRTIO_NET_F_GUEST_CSUM) + | (1 << VIRTIO_NET_F_GUEST_UFO) + | (1 << VIRTIO_NET_F_GUEST_TSO4) + | (1 << VIRTIO_NET_F_GUEST_TSO6); + let expected_tap_features = generated::TUN_F_CSUM + | generated::TUN_F_UFO + | generated::TUN_F_TSO4 + | generated::TUN_F_TSO6; let supported_flags = Net::build_tap_offload_features(supported_features); assert_eq!(supported_flags, expected_tap_features); @@ -1164,9 +1166,9 @@ pub mod tests { // Same as before, however, using each supported feature one by one. fn test_build_tap_offload_features_one_by_one() { let features = [ - (1 << VIRTIO_NET_F_GUEST_CSUM, gen::TUN_F_CSUM), - (1 << VIRTIO_NET_F_GUEST_UFO, gen::TUN_F_UFO), - (1 << VIRTIO_NET_F_GUEST_TSO4, gen::TUN_F_TSO4), + (1 << VIRTIO_NET_F_GUEST_CSUM, generated::TUN_F_CSUM), + (1 << VIRTIO_NET_F_GUEST_UFO, generated::TUN_F_UFO), + (1 << VIRTIO_NET_F_GUEST_TSO4, generated::TUN_F_TSO4), ]; for (virtio_flag, tap_flag) in features { let supported_flags = Net::build_tap_offload_features(virtio_flag); @@ -1809,6 +1811,9 @@ pub mod tests { assert_eq!(th.txq.used.idx.get(), 1); assert!(&th.net().irq_trigger.has_pending_irq(IrqType::Vring)); th.txq.check_used_elem(0, 0, 0); + + // dropping th would double close the tap fd, so leak it + std::mem::forget(th); } #[test] @@ -1917,16 +1922,18 @@ pub mod tests { check_metric_after_block!( &METRICS.mmds.rx_accepted, 1, - assert!(Net::write_to_mmds_or_tap( - net.mmds_ns.as_mut(), - &mut net.tx_rate_limiter, - &mut headers, - &buffer, - &mut net.tap, - Some(src_mac), - &net.metrics, + assert!( + Net::write_to_mmds_or_tap( + net.mmds_ns.as_mut(), + &mut net.tx_rate_limiter, + &mut headers, + &buffer, + &mut net.tap, + Some(src_mac), + &net.metrics, + ) + .unwrap() ) - .unwrap()) ); // Validate that MMDS has a response and we can retrieve it. @@ -2042,6 +2049,9 @@ pub mod tests { 1, th.simulate_event(NetEvent::Tap) ); + + // dropping th would double close the tap fd, so leak it + std::mem::forget(th); } #[test] @@ -2153,7 +2163,7 @@ pub mod tests { // Test RX bandwidth rate limiting { // create bandwidth rate limiter that allows 2000 bytes/s with bucket size 1000 bytes - let mut rl = RateLimiter::new(1000, 0, 500, 0, 0, 0).unwrap(); + let mut rl = RateLimiter::new(1000, 0, 1000, 0, 0, 0).unwrap(); // set up RX assert!(th.net().rx_buffer.used_descriptors == 0); @@ -2195,9 +2205,9 @@ pub mod tests { assert_eq!(th.net().metrics.rx_rate_limiter_throttled.count(), 2); } - // wait for 500ms to give the rate-limiter timer a chance to replenish - // wait for an extra 500ms to make sure the timerfd event makes its way from the kernel - thread::sleep(Duration::from_millis(1000)); + // wait for 1000ms to give the rate-limiter timer a chance to replenish + // wait for an extra 1000ms to make sure the timerfd event makes its way from the kernel + thread::sleep(Duration::from_millis(2000)); // following RX procedure should succeed because bandwidth should now be available { @@ -2276,7 +2286,7 @@ pub mod tests { // Test RX ops rate limiting { // create ops rate limiter that allows 2 ops/s with bucket size 1 ops - let mut rl = RateLimiter::new(0, 0, 0, 1, 0, 500).unwrap(); + let mut rl = RateLimiter::new(0, 0, 0, 1, 0, 1000).unwrap(); // set up RX assert!(th.net().rx_buffer.used_descriptors == 0); @@ -2319,9 +2329,9 @@ pub mod tests { assert_eq!(th.rxq.used.idx.get(), 0); } - // wait for 500ms to give the rate-limiter timer a chance to replenish - // wait for an extra 500ms to make sure the timerfd event makes its way from the kernel - thread::sleep(Duration::from_millis(1000)); + // wait for 1000ms to give the rate-limiter timer a chance to replenish + // wait for an extra 1000ms to make sure the timerfd event makes its way from the kernel + thread::sleep(Duration::from_millis(2000)); // following RX procedure should succeed because ops should now be available { diff --git a/src/vmm/src/devices/virtio/net/event_handler.rs b/src/vmm/src/devices/virtio/net/event_handler.rs index 1d8805e93fe..9d8c09a45f2 100644 --- a/src/vmm/src/devices/virtio/net/event_handler.rs +++ b/src/vmm/src/devices/virtio/net/event_handler.rs @@ -7,7 +7,7 @@ use vmm_sys_util::epoll::EventSet; use crate::devices::virtio::device::VirtioDevice; use crate::devices::virtio::net::device::Net; use crate::devices::virtio::net::{RX_INDEX, TX_INDEX}; -use crate::logger::{error, warn, IncMetric}; +use crate::logger::{IncMetric, error, warn}; impl Net { const PROCESS_ACTIVATE: u32 = 0; @@ -132,8 +132,8 @@ impl MutEventSubscriber for Net { #[cfg(test)] pub mod tests { - use crate::devices::virtio::net::test_utils::test::TestHelper; use crate::devices::virtio::net::test_utils::NetQueue; + use crate::devices::virtio::net::test_utils::test::TestHelper; use crate::devices::virtio::net::{MAX_BUFFER_SIZE, TX_INDEX}; use crate::test_utils::single_region_mem; diff --git a/src/vmm/src/devices/virtio/net/gen/iff.rs b/src/vmm/src/devices/virtio/net/gen/iff.rs deleted file mode 100644 index 868c61cda73..00000000000 --- a/src/vmm/src/devices/virtio/net/gen/iff.rs +++ /dev/null @@ -1,1187 +0,0 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// automatically generated by tools/bindgen.sh - -#![allow( - non_camel_case_types, - non_upper_case_globals, - dead_code, - non_snake_case, - clippy::ptr_as_ptr, - clippy::undocumented_unsafe_blocks, - missing_debug_implementations, - clippy::tests_outside_test_module -)] - -pub const IFNAMSIZ: u32 = 16; -pub const IFALIASZ: u32 = 256; -pub const IF_GET_IFACE: u32 = 1; -pub const IF_GET_PROTO: u32 = 2; -pub const IF_IFACE_V35: u32 = 4096; -pub const IF_IFACE_V24: u32 = 4097; -pub const IF_IFACE_X21: u32 = 4098; -pub const IF_IFACE_T1: u32 = 4099; -pub const IF_IFACE_E1: u32 = 4100; -pub const IF_IFACE_SYNC_SERIAL: u32 = 4101; -pub const IF_IFACE_X21D: u32 = 4102; -pub const IF_PROTO_HDLC: u32 = 8192; -pub const IF_PROTO_PPP: u32 = 8193; -pub const IF_PROTO_CISCO: u32 = 8194; -pub const IF_PROTO_FR: u32 = 8195; -pub const IF_PROTO_FR_ADD_PVC: u32 = 8196; -pub const IF_PROTO_FR_DEL_PVC: u32 = 8197; -pub const IF_PROTO_X25: u32 = 8198; -pub const IF_PROTO_HDLC_ETH: u32 = 8199; -pub const IF_PROTO_FR_ADD_ETH_PVC: u32 = 8200; -pub const IF_PROTO_FR_DEL_ETH_PVC: u32 = 8201; -pub const IF_PROTO_FR_PVC: u32 = 8202; -pub const IF_PROTO_FR_ETH_PVC: u32 = 8203; -pub const IF_PROTO_RAW: u32 = 8204; -pub const IFHWADDRLEN: u32 = 6; -pub type sa_family_t = ::std::os::raw::c_ushort; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct sockaddr { - pub sa_family: sa_family_t, - pub sa_data: [::std::os::raw::c_char; 14usize], -} -#[test] -fn bindgen_test_layout_sockaddr() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(sockaddr)) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!("Alignment of ", stringify!(sockaddr)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sa_family) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(sockaddr), - "::", - stringify!(sa_family) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sa_data) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(sockaddr), - "::", - stringify!(sa_data) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct sync_serial_settings { - pub clock_rate: ::std::os::raw::c_uint, - pub clock_type: ::std::os::raw::c_uint, - pub loopback: ::std::os::raw::c_ushort, -} -#[test] -fn bindgen_test_layout_sync_serial_settings() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 12usize, - concat!("Size of: ", stringify!(sync_serial_settings)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(sync_serial_settings)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).clock_rate) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(sync_serial_settings), - "::", - stringify!(clock_rate) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).clock_type) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(sync_serial_settings), - "::", - stringify!(clock_type) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).loopback) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(sync_serial_settings), - "::", - stringify!(loopback) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct te1_settings { - pub clock_rate: ::std::os::raw::c_uint, - pub clock_type: ::std::os::raw::c_uint, - pub loopback: ::std::os::raw::c_ushort, - pub slot_map: ::std::os::raw::c_uint, -} -#[test] -fn bindgen_test_layout_te1_settings() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(te1_settings)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(te1_settings)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).clock_rate) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(te1_settings), - "::", - stringify!(clock_rate) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).clock_type) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(te1_settings), - "::", - stringify!(clock_type) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).loopback) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(te1_settings), - "::", - stringify!(loopback) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).slot_map) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(te1_settings), - "::", - stringify!(slot_map) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct raw_hdlc_proto { - pub encoding: ::std::os::raw::c_ushort, - pub parity: ::std::os::raw::c_ushort, -} -#[test] -fn bindgen_test_layout_raw_hdlc_proto() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 4usize, - concat!("Size of: ", stringify!(raw_hdlc_proto)) - ); - assert_eq!( - ::std::mem::align_of::(), - 2usize, - concat!("Alignment of ", stringify!(raw_hdlc_proto)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).encoding) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(raw_hdlc_proto), - "::", - stringify!(encoding) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).parity) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(raw_hdlc_proto), - "::", - stringify!(parity) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct fr_proto { - pub t391: ::std::os::raw::c_uint, - pub t392: ::std::os::raw::c_uint, - pub n391: ::std::os::raw::c_uint, - pub n392: ::std::os::raw::c_uint, - pub n393: ::std::os::raw::c_uint, - pub lmi: ::std::os::raw::c_ushort, - pub dce: ::std::os::raw::c_ushort, -} -#[test] -fn bindgen_test_layout_fr_proto() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 24usize, - concat!("Size of: ", stringify!(fr_proto)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(fr_proto)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).t391) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(fr_proto), - "::", - stringify!(t391) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).t392) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(fr_proto), - "::", - stringify!(t392) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).n391) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(fr_proto), - "::", - stringify!(n391) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).n392) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(fr_proto), - "::", - stringify!(n392) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).n393) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(fr_proto), - "::", - stringify!(n393) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).lmi) as usize - ptr as usize }, - 20usize, - concat!( - "Offset of field: ", - stringify!(fr_proto), - "::", - stringify!(lmi) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dce) as usize - ptr as usize }, - 22usize, - concat!( - "Offset of field: ", - stringify!(fr_proto), - "::", - stringify!(dce) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct fr_proto_pvc { - pub dlci: ::std::os::raw::c_uint, -} -#[test] -fn bindgen_test_layout_fr_proto_pvc() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 4usize, - concat!("Size of: ", stringify!(fr_proto_pvc)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(fr_proto_pvc)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dlci) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(fr_proto_pvc), - "::", - stringify!(dlci) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct fr_proto_pvc_info { - pub dlci: ::std::os::raw::c_uint, - pub master: [::std::os::raw::c_char; 16usize], -} -#[test] -fn bindgen_test_layout_fr_proto_pvc_info() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 20usize, - concat!("Size of: ", stringify!(fr_proto_pvc_info)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(fr_proto_pvc_info)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dlci) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(fr_proto_pvc_info), - "::", - stringify!(dlci) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).master) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(fr_proto_pvc_info), - "::", - stringify!(master) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct cisco_proto { - pub interval: ::std::os::raw::c_uint, - pub timeout: ::std::os::raw::c_uint, -} -#[test] -fn bindgen_test_layout_cisco_proto() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(cisco_proto)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(cisco_proto)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).interval) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(cisco_proto), - "::", - stringify!(interval) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).timeout) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(cisco_proto), - "::", - stringify!(timeout) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct x25_hdlc_proto { - pub dce: ::std::os::raw::c_ushort, - pub modulo: ::std::os::raw::c_uint, - pub window: ::std::os::raw::c_uint, - pub t1: ::std::os::raw::c_uint, - pub t2: ::std::os::raw::c_uint, - pub n2: ::std::os::raw::c_uint, -} -#[test] -fn bindgen_test_layout_x25_hdlc_proto() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 24usize, - concat!("Size of: ", stringify!(x25_hdlc_proto)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(x25_hdlc_proto)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dce) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(x25_hdlc_proto), - "::", - stringify!(dce) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).modulo) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(x25_hdlc_proto), - "::", - stringify!(modulo) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).window) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(x25_hdlc_proto), - "::", - stringify!(window) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).t1) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(x25_hdlc_proto), - "::", - stringify!(t1) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).t2) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(x25_hdlc_proto), - "::", - stringify!(t2) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).n2) as usize - ptr as usize }, - 20usize, - concat!( - "Offset of field: ", - stringify!(x25_hdlc_proto), - "::", - stringify!(n2) - ) - ); -} -pub const net_device_flags_IFF_UP: net_device_flags = 1; -pub const net_device_flags_IFF_BROADCAST: net_device_flags = 2; -pub const net_device_flags_IFF_DEBUG: net_device_flags = 4; -pub const net_device_flags_IFF_LOOPBACK: net_device_flags = 8; -pub const net_device_flags_IFF_POINTOPOINT: net_device_flags = 16; -pub const net_device_flags_IFF_NOTRAILERS: net_device_flags = 32; -pub const net_device_flags_IFF_RUNNING: net_device_flags = 64; -pub const net_device_flags_IFF_NOARP: net_device_flags = 128; -pub const net_device_flags_IFF_PROMISC: net_device_flags = 256; -pub const net_device_flags_IFF_ALLMULTI: net_device_flags = 512; -pub const net_device_flags_IFF_MASTER: net_device_flags = 1024; -pub const net_device_flags_IFF_SLAVE: net_device_flags = 2048; -pub const net_device_flags_IFF_MULTICAST: net_device_flags = 4096; -pub const net_device_flags_IFF_PORTSEL: net_device_flags = 8192; -pub const net_device_flags_IFF_AUTOMEDIA: net_device_flags = 16384; -pub const net_device_flags_IFF_DYNAMIC: net_device_flags = 32768; -pub const net_device_flags_IFF_LOWER_UP: net_device_flags = 65536; -pub const net_device_flags_IFF_DORMANT: net_device_flags = 131072; -pub const net_device_flags_IFF_ECHO: net_device_flags = 262144; -pub type net_device_flags = ::std::os::raw::c_uint; -pub const IF_OPER_UNKNOWN: _bindgen_ty_4 = 0; -pub const IF_OPER_NOTPRESENT: _bindgen_ty_4 = 1; -pub const IF_OPER_DOWN: _bindgen_ty_4 = 2; -pub const IF_OPER_LOWERLAYERDOWN: _bindgen_ty_4 = 3; -pub const IF_OPER_TESTING: _bindgen_ty_4 = 4; -pub const IF_OPER_DORMANT: _bindgen_ty_4 = 5; -pub const IF_OPER_UP: _bindgen_ty_4 = 6; -pub type _bindgen_ty_4 = ::std::os::raw::c_uint; -pub const IF_LINK_MODE_DEFAULT: _bindgen_ty_5 = 0; -pub const IF_LINK_MODE_DORMANT: _bindgen_ty_5 = 1; -pub const IF_LINK_MODE_TESTING: _bindgen_ty_5 = 2; -pub type _bindgen_ty_5 = ::std::os::raw::c_uint; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct ifmap { - pub mem_start: ::std::os::raw::c_ulong, - pub mem_end: ::std::os::raw::c_ulong, - pub base_addr: ::std::os::raw::c_ushort, - pub irq: ::std::os::raw::c_uchar, - pub dma: ::std::os::raw::c_uchar, - pub port: ::std::os::raw::c_uchar, -} -#[test] -fn bindgen_test_layout_ifmap() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 24usize, - concat!("Size of: ", stringify!(ifmap)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(ifmap)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).mem_start) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifmap), - "::", - stringify!(mem_start) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).mem_end) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(ifmap), - "::", - stringify!(mem_end) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).base_addr) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(ifmap), - "::", - stringify!(base_addr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).irq) as usize - ptr as usize }, - 18usize, - concat!( - "Offset of field: ", - stringify!(ifmap), - "::", - stringify!(irq) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dma) as usize - ptr as usize }, - 19usize, - concat!( - "Offset of field: ", - stringify!(ifmap), - "::", - stringify!(dma) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).port) as usize - ptr as usize }, - 20usize, - concat!( - "Offset of field: ", - stringify!(ifmap), - "::", - stringify!(port) - ) - ); -} -#[repr(C)] -#[derive(Copy, Clone)] -pub struct if_settings { - pub type_: ::std::os::raw::c_uint, - pub size: ::std::os::raw::c_uint, - pub ifs_ifsu: if_settings__bindgen_ty_1, -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union if_settings__bindgen_ty_1 { - pub raw_hdlc: *mut raw_hdlc_proto, - pub cisco: *mut cisco_proto, - pub fr: *mut fr_proto, - pub fr_pvc: *mut fr_proto_pvc, - pub fr_pvc_info: *mut fr_proto_pvc_info, - pub x25: *mut x25_hdlc_proto, - pub sync: *mut sync_serial_settings, - pub te1: *mut te1_settings, -} -#[test] -fn bindgen_test_layout_if_settings__bindgen_ty_1() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(if_settings__bindgen_ty_1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(if_settings__bindgen_ty_1)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).raw_hdlc) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(if_settings__bindgen_ty_1), - "::", - stringify!(raw_hdlc) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).cisco) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(if_settings__bindgen_ty_1), - "::", - stringify!(cisco) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).fr) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(if_settings__bindgen_ty_1), - "::", - stringify!(fr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).fr_pvc) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(if_settings__bindgen_ty_1), - "::", - stringify!(fr_pvc) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).fr_pvc_info) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(if_settings__bindgen_ty_1), - "::", - stringify!(fr_pvc_info) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).x25) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(if_settings__bindgen_ty_1), - "::", - stringify!(x25) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sync) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(if_settings__bindgen_ty_1), - "::", - stringify!(sync) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).te1) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(if_settings__bindgen_ty_1), - "::", - stringify!(te1) - ) - ); -} -impl Default for if_settings__bindgen_ty_1 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[test] -fn bindgen_test_layout_if_settings() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(if_settings)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(if_settings)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).type_) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(if_settings), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).size) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(if_settings), - "::", - stringify!(size) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifs_ifsu) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(if_settings), - "::", - stringify!(ifs_ifsu) - ) - ); -} -impl Default for if_settings { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ifreq { - pub ifr_ifrn: ifreq__bindgen_ty_1, - pub ifr_ifru: ifreq__bindgen_ty_2, -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union ifreq__bindgen_ty_1 { - pub ifrn_name: [::std::os::raw::c_uchar; 16usize], -} -#[test] -fn bindgen_test_layout_ifreq__bindgen_ty_1() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(ifreq__bindgen_ty_1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 1usize, - concat!("Alignment of ", stringify!(ifreq__bindgen_ty_1)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifrn_name) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_1), - "::", - stringify!(ifrn_name) - ) - ); -} -impl Default for ifreq__bindgen_ty_1 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union ifreq__bindgen_ty_2 { - pub ifru_addr: sockaddr, - pub ifru_dstaddr: sockaddr, - pub ifru_broadaddr: sockaddr, - pub ifru_netmask: sockaddr, - pub ifru_hwaddr: sockaddr, - pub ifru_flags: ::std::os::raw::c_short, - pub ifru_ivalue: ::std::os::raw::c_int, - pub ifru_mtu: ::std::os::raw::c_int, - pub ifru_map: ifmap, - pub ifru_slave: [::std::os::raw::c_char; 16usize], - pub ifru_newname: [::std::os::raw::c_char; 16usize], - pub ifru_data: *mut ::std::os::raw::c_void, - pub ifru_settings: if_settings, -} -#[test] -fn bindgen_test_layout_ifreq__bindgen_ty_2() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 24usize, - concat!("Size of: ", stringify!(ifreq__bindgen_ty_2)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(ifreq__bindgen_ty_2)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_addr) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_addr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_dstaddr) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_dstaddr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_broadaddr) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_broadaddr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_netmask) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_netmask) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_hwaddr) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_hwaddr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_ivalue) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_ivalue) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_mtu) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_mtu) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_map) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_map) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_slave) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_slave) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_newname) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_newname) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_data) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_data) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifru_settings) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq__bindgen_ty_2), - "::", - stringify!(ifru_settings) - ) - ); -} -impl Default for ifreq__bindgen_ty_2 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[test] -fn bindgen_test_layout_ifreq() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 40usize, - concat!("Size of: ", stringify!(ifreq)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(ifreq)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifr_ifrn) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifreq), - "::", - stringify!(ifr_ifrn) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifr_ifru) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(ifreq), - "::", - stringify!(ifr_ifru) - ) - ); -} -impl Default for ifreq { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ifconf { - pub ifc_len: ::std::os::raw::c_int, - pub ifc_ifcu: ifconf__bindgen_ty_1, -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union ifconf__bindgen_ty_1 { - pub ifcu_buf: *mut ::std::os::raw::c_char, - pub ifcu_req: *mut ifreq, -} -#[test] -fn bindgen_test_layout_ifconf__bindgen_ty_1() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(ifconf__bindgen_ty_1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(ifconf__bindgen_ty_1)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifcu_buf) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifconf__bindgen_ty_1), - "::", - stringify!(ifcu_buf) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifcu_req) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifconf__bindgen_ty_1), - "::", - stringify!(ifcu_req) - ) - ); -} -impl Default for ifconf__bindgen_ty_1 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[test] -fn bindgen_test_layout_ifconf() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(ifconf)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(ifconf)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifc_len) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(ifconf), - "::", - stringify!(ifc_len) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ifc_ifcu) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(ifconf), - "::", - stringify!(ifc_ifcu) - ) - ); -} -impl Default for ifconf { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} diff --git a/src/vmm/src/devices/virtio/net/gen/if_tun.rs b/src/vmm/src/devices/virtio/net/generated/if_tun.rs similarity index 66% rename from src/vmm/src/devices/virtio/net/gen/if_tun.rs rename to src/vmm/src/devices/virtio/net/generated/if_tun.rs index 7b994a327c1..d08fe1b1ce7 100644 --- a/src/vmm/src/devices/virtio/net/gen/if_tun.rs +++ b/src/vmm/src/devices/virtio/net/generated/if_tun.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,7 +11,8 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] pub const ETH_ALEN: u32 = 6; @@ -62,7 +63,10 @@ pub const ETH_P_PPP_SES: u32 = 34916; pub const ETH_P_LINK_CTL: u32 = 34924; pub const ETH_P_ATMFATE: u32 = 34948; pub const ETH_P_PAE: u32 = 34958; +pub const ETH_P_PROFINET: u32 = 34962; +pub const ETH_P_REALTEK: u32 = 34969; pub const ETH_P_AOE: u32 = 34978; +pub const ETH_P_ETHERCAT: u32 = 34980; pub const ETH_P_8021AD: u32 = 34984; pub const ETH_P_802_EX1: u32 = 34997; pub const ETH_P_PREAUTH: u32 = 35015; @@ -89,6 +93,7 @@ pub const ETH_P_QINQ2: u32 = 37376; pub const ETH_P_QINQ3: u32 = 37632; pub const ETH_P_EDSA: u32 = 56026; pub const ETH_P_DSA_8021Q: u32 = 56027; +pub const ETH_P_DSA_A5PSW: u32 = 57345; pub const ETH_P_IFE: u32 = 60734; pub const ETH_P_AF_IUCV: u32 = 64507; pub const ETH_P_802_3_MIN: u32 = 1536; @@ -103,6 +108,7 @@ pub const ETH_P_PPP_MP: u32 = 8; pub const ETH_P_LOCALTALK: u32 = 9; pub const ETH_P_CAN: u32 = 12; pub const ETH_P_CANFD: u32 = 13; +pub const ETH_P_CANXL: u32 = 14; pub const ETH_P_PPPTALK: u32 = 16; pub const ETH_P_TR_802_2: u32 = 17; pub const ETH_P_MOBITEX: u32 = 21; @@ -131,6 +137,8 @@ pub const TUN_F_TSO4: u32 = 2; pub const TUN_F_TSO6: u32 = 4; pub const TUN_F_TSO_ECN: u32 = 8; pub const TUN_F_UFO: u32 = 16; +pub const TUN_F_USO4: u32 = 32; +pub const TUN_F_USO6: u32 = 64; pub const TUN_PKT_STRIP: u32 = 1; pub const TUN_FLT_ALLMULTI: u32 = 1; pub type __u8 = ::std::os::raw::c_uchar; @@ -144,102 +152,28 @@ pub struct sock_filter { pub jf: __u8, pub k: __u32, } -#[test] -fn bindgen_test_layout_sock_filter() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(sock_filter)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(sock_filter)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).code) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(sock_filter), - "::", - stringify!(code) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).jt) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(sock_filter), - "::", - stringify!(jt) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).jf) as usize - ptr as usize }, - 3usize, - concat!( - "Offset of field: ", - stringify!(sock_filter), - "::", - stringify!(jf) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).k) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(sock_filter), - "::", - stringify!(k) - ) - ); -} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sock_filter"][::std::mem::size_of::() - 8usize]; + ["Alignment of sock_filter"][::std::mem::align_of::() - 4usize]; + ["Offset of field: sock_filter::code"][::std::mem::offset_of!(sock_filter, code) - 0usize]; + ["Offset of field: sock_filter::jt"][::std::mem::offset_of!(sock_filter, jt) - 2usize]; + ["Offset of field: sock_filter::jf"][::std::mem::offset_of!(sock_filter, jf) - 3usize]; + ["Offset of field: sock_filter::k"][::std::mem::offset_of!(sock_filter, k) - 4usize]; +}; #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq)] pub struct sock_fprog { pub len: ::std::os::raw::c_ushort, pub filter: *mut sock_filter, } -#[test] -fn bindgen_test_layout_sock_fprog() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(sock_fprog)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(sock_fprog)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).len) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(sock_fprog), - "::", - stringify!(len) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).filter) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(sock_fprog), - "::", - stringify!(filter) - ) - ); -} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sock_fprog"][::std::mem::size_of::() - 16usize]; + ["Alignment of sock_fprog"][::std::mem::align_of::() - 8usize]; + ["Offset of field: sock_fprog::len"][::std::mem::offset_of!(sock_fprog, len) - 0usize]; + ["Offset of field: sock_fprog::filter"][::std::mem::offset_of!(sock_fprog, filter) - 8usize]; +}; impl Default for sock_fprog { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); diff --git a/src/vmm/src/devices/virtio/net/generated/iff.rs b/src/vmm/src/devices/virtio/net/generated/iff.rs new file mode 100644 index 00000000000..c805642a834 --- /dev/null +++ b/src/vmm/src/devices/virtio/net/generated/iff.rs @@ -0,0 +1,465 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// automatically generated by tools/bindgen.sh + +#![allow( + non_camel_case_types, + non_upper_case_globals, + dead_code, + non_snake_case, + clippy::ptr_as_ptr, + clippy::undocumented_unsafe_blocks, + missing_debug_implementations, + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn +)] + +pub const IFNAMSIZ: u32 = 16; +pub const IFALIASZ: u32 = 256; +pub const IF_GET_IFACE: u32 = 1; +pub const IF_GET_PROTO: u32 = 2; +pub const IF_IFACE_V35: u32 = 4096; +pub const IF_IFACE_V24: u32 = 4097; +pub const IF_IFACE_X21: u32 = 4098; +pub const IF_IFACE_T1: u32 = 4099; +pub const IF_IFACE_E1: u32 = 4100; +pub const IF_IFACE_SYNC_SERIAL: u32 = 4101; +pub const IF_IFACE_X21D: u32 = 4102; +pub const IF_PROTO_HDLC: u32 = 8192; +pub const IF_PROTO_PPP: u32 = 8193; +pub const IF_PROTO_CISCO: u32 = 8194; +pub const IF_PROTO_FR: u32 = 8195; +pub const IF_PROTO_FR_ADD_PVC: u32 = 8196; +pub const IF_PROTO_FR_DEL_PVC: u32 = 8197; +pub const IF_PROTO_X25: u32 = 8198; +pub const IF_PROTO_HDLC_ETH: u32 = 8199; +pub const IF_PROTO_FR_ADD_ETH_PVC: u32 = 8200; +pub const IF_PROTO_FR_DEL_ETH_PVC: u32 = 8201; +pub const IF_PROTO_FR_PVC: u32 = 8202; +pub const IF_PROTO_FR_ETH_PVC: u32 = 8203; +pub const IF_PROTO_RAW: u32 = 8204; +pub const IFHWADDRLEN: u32 = 6; +pub type sa_family_t = ::std::os::raw::c_ushort; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct sockaddr { + pub sa_family: sa_family_t, + pub sa_data: [::std::os::raw::c_char; 14usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sockaddr"][::std::mem::size_of::() - 16usize]; + ["Alignment of sockaddr"][::std::mem::align_of::() - 2usize]; + ["Offset of field: sockaddr::sa_family"][::std::mem::offset_of!(sockaddr, sa_family) - 0usize]; + ["Offset of field: sockaddr::sa_data"][::std::mem::offset_of!(sockaddr, sa_data) - 2usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct sync_serial_settings { + pub clock_rate: ::std::os::raw::c_uint, + pub clock_type: ::std::os::raw::c_uint, + pub loopback: ::std::os::raw::c_ushort, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sync_serial_settings"][::std::mem::size_of::() - 12usize]; + ["Alignment of sync_serial_settings"][::std::mem::align_of::() - 4usize]; + ["Offset of field: sync_serial_settings::clock_rate"] + [::std::mem::offset_of!(sync_serial_settings, clock_rate) - 0usize]; + ["Offset of field: sync_serial_settings::clock_type"] + [::std::mem::offset_of!(sync_serial_settings, clock_type) - 4usize]; + ["Offset of field: sync_serial_settings::loopback"] + [::std::mem::offset_of!(sync_serial_settings, loopback) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct te1_settings { + pub clock_rate: ::std::os::raw::c_uint, + pub clock_type: ::std::os::raw::c_uint, + pub loopback: ::std::os::raw::c_ushort, + pub slot_map: ::std::os::raw::c_uint, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of te1_settings"][::std::mem::size_of::() - 16usize]; + ["Alignment of te1_settings"][::std::mem::align_of::() - 4usize]; + ["Offset of field: te1_settings::clock_rate"] + [::std::mem::offset_of!(te1_settings, clock_rate) - 0usize]; + ["Offset of field: te1_settings::clock_type"] + [::std::mem::offset_of!(te1_settings, clock_type) - 4usize]; + ["Offset of field: te1_settings::loopback"] + [::std::mem::offset_of!(te1_settings, loopback) - 8usize]; + ["Offset of field: te1_settings::slot_map"] + [::std::mem::offset_of!(te1_settings, slot_map) - 12usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct raw_hdlc_proto { + pub encoding: ::std::os::raw::c_ushort, + pub parity: ::std::os::raw::c_ushort, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of raw_hdlc_proto"][::std::mem::size_of::() - 4usize]; + ["Alignment of raw_hdlc_proto"][::std::mem::align_of::() - 2usize]; + ["Offset of field: raw_hdlc_proto::encoding"] + [::std::mem::offset_of!(raw_hdlc_proto, encoding) - 0usize]; + ["Offset of field: raw_hdlc_proto::parity"] + [::std::mem::offset_of!(raw_hdlc_proto, parity) - 2usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct fr_proto { + pub t391: ::std::os::raw::c_uint, + pub t392: ::std::os::raw::c_uint, + pub n391: ::std::os::raw::c_uint, + pub n392: ::std::os::raw::c_uint, + pub n393: ::std::os::raw::c_uint, + pub lmi: ::std::os::raw::c_ushort, + pub dce: ::std::os::raw::c_ushort, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of fr_proto"][::std::mem::size_of::() - 24usize]; + ["Alignment of fr_proto"][::std::mem::align_of::() - 4usize]; + ["Offset of field: fr_proto::t391"][::std::mem::offset_of!(fr_proto, t391) - 0usize]; + ["Offset of field: fr_proto::t392"][::std::mem::offset_of!(fr_proto, t392) - 4usize]; + ["Offset of field: fr_proto::n391"][::std::mem::offset_of!(fr_proto, n391) - 8usize]; + ["Offset of field: fr_proto::n392"][::std::mem::offset_of!(fr_proto, n392) - 12usize]; + ["Offset of field: fr_proto::n393"][::std::mem::offset_of!(fr_proto, n393) - 16usize]; + ["Offset of field: fr_proto::lmi"][::std::mem::offset_of!(fr_proto, lmi) - 20usize]; + ["Offset of field: fr_proto::dce"][::std::mem::offset_of!(fr_proto, dce) - 22usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct fr_proto_pvc { + pub dlci: ::std::os::raw::c_uint, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of fr_proto_pvc"][::std::mem::size_of::() - 4usize]; + ["Alignment of fr_proto_pvc"][::std::mem::align_of::() - 4usize]; + ["Offset of field: fr_proto_pvc::dlci"][::std::mem::offset_of!(fr_proto_pvc, dlci) - 0usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct fr_proto_pvc_info { + pub dlci: ::std::os::raw::c_uint, + pub master: [::std::os::raw::c_char; 16usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of fr_proto_pvc_info"][::std::mem::size_of::() - 20usize]; + ["Alignment of fr_proto_pvc_info"][::std::mem::align_of::() - 4usize]; + ["Offset of field: fr_proto_pvc_info::dlci"] + [::std::mem::offset_of!(fr_proto_pvc_info, dlci) - 0usize]; + ["Offset of field: fr_proto_pvc_info::master"] + [::std::mem::offset_of!(fr_proto_pvc_info, master) - 4usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct cisco_proto { + pub interval: ::std::os::raw::c_uint, + pub timeout: ::std::os::raw::c_uint, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of cisco_proto"][::std::mem::size_of::() - 8usize]; + ["Alignment of cisco_proto"][::std::mem::align_of::() - 4usize]; + ["Offset of field: cisco_proto::interval"] + [::std::mem::offset_of!(cisco_proto, interval) - 0usize]; + ["Offset of field: cisco_proto::timeout"] + [::std::mem::offset_of!(cisco_proto, timeout) - 4usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct x25_hdlc_proto { + pub dce: ::std::os::raw::c_ushort, + pub modulo: ::std::os::raw::c_uint, + pub window: ::std::os::raw::c_uint, + pub t1: ::std::os::raw::c_uint, + pub t2: ::std::os::raw::c_uint, + pub n2: ::std::os::raw::c_uint, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of x25_hdlc_proto"][::std::mem::size_of::() - 24usize]; + ["Alignment of x25_hdlc_proto"][::std::mem::align_of::() - 4usize]; + ["Offset of field: x25_hdlc_proto::dce"][::std::mem::offset_of!(x25_hdlc_proto, dce) - 0usize]; + ["Offset of field: x25_hdlc_proto::modulo"] + [::std::mem::offset_of!(x25_hdlc_proto, modulo) - 4usize]; + ["Offset of field: x25_hdlc_proto::window"] + [::std::mem::offset_of!(x25_hdlc_proto, window) - 8usize]; + ["Offset of field: x25_hdlc_proto::t1"][::std::mem::offset_of!(x25_hdlc_proto, t1) - 12usize]; + ["Offset of field: x25_hdlc_proto::t2"][::std::mem::offset_of!(x25_hdlc_proto, t2) - 16usize]; + ["Offset of field: x25_hdlc_proto::n2"][::std::mem::offset_of!(x25_hdlc_proto, n2) - 20usize]; +}; +pub const net_device_flags_IFF_UP: net_device_flags = 1; +pub const net_device_flags_IFF_BROADCAST: net_device_flags = 2; +pub const net_device_flags_IFF_DEBUG: net_device_flags = 4; +pub const net_device_flags_IFF_LOOPBACK: net_device_flags = 8; +pub const net_device_flags_IFF_POINTOPOINT: net_device_flags = 16; +pub const net_device_flags_IFF_NOTRAILERS: net_device_flags = 32; +pub const net_device_flags_IFF_RUNNING: net_device_flags = 64; +pub const net_device_flags_IFF_NOARP: net_device_flags = 128; +pub const net_device_flags_IFF_PROMISC: net_device_flags = 256; +pub const net_device_flags_IFF_ALLMULTI: net_device_flags = 512; +pub const net_device_flags_IFF_MASTER: net_device_flags = 1024; +pub const net_device_flags_IFF_SLAVE: net_device_flags = 2048; +pub const net_device_flags_IFF_MULTICAST: net_device_flags = 4096; +pub const net_device_flags_IFF_PORTSEL: net_device_flags = 8192; +pub const net_device_flags_IFF_AUTOMEDIA: net_device_flags = 16384; +pub const net_device_flags_IFF_DYNAMIC: net_device_flags = 32768; +pub const net_device_flags_IFF_LOWER_UP: net_device_flags = 65536; +pub const net_device_flags_IFF_DORMANT: net_device_flags = 131072; +pub const net_device_flags_IFF_ECHO: net_device_flags = 262144; +pub type net_device_flags = ::std::os::raw::c_uint; +pub const IF_OPER_UNKNOWN: _bindgen_ty_4 = 0; +pub const IF_OPER_NOTPRESENT: _bindgen_ty_4 = 1; +pub const IF_OPER_DOWN: _bindgen_ty_4 = 2; +pub const IF_OPER_LOWERLAYERDOWN: _bindgen_ty_4 = 3; +pub const IF_OPER_TESTING: _bindgen_ty_4 = 4; +pub const IF_OPER_DORMANT: _bindgen_ty_4 = 5; +pub const IF_OPER_UP: _bindgen_ty_4 = 6; +pub type _bindgen_ty_4 = ::std::os::raw::c_uint; +pub const IF_LINK_MODE_DEFAULT: _bindgen_ty_5 = 0; +pub const IF_LINK_MODE_DORMANT: _bindgen_ty_5 = 1; +pub const IF_LINK_MODE_TESTING: _bindgen_ty_5 = 2; +pub type _bindgen_ty_5 = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct ifmap { + pub mem_start: ::std::os::raw::c_ulong, + pub mem_end: ::std::os::raw::c_ulong, + pub base_addr: ::std::os::raw::c_ushort, + pub irq: ::std::os::raw::c_uchar, + pub dma: ::std::os::raw::c_uchar, + pub port: ::std::os::raw::c_uchar, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ifmap"][::std::mem::size_of::() - 24usize]; + ["Alignment of ifmap"][::std::mem::align_of::() - 8usize]; + ["Offset of field: ifmap::mem_start"][::std::mem::offset_of!(ifmap, mem_start) - 0usize]; + ["Offset of field: ifmap::mem_end"][::std::mem::offset_of!(ifmap, mem_end) - 8usize]; + ["Offset of field: ifmap::base_addr"][::std::mem::offset_of!(ifmap, base_addr) - 16usize]; + ["Offset of field: ifmap::irq"][::std::mem::offset_of!(ifmap, irq) - 18usize]; + ["Offset of field: ifmap::dma"][::std::mem::offset_of!(ifmap, dma) - 19usize]; + ["Offset of field: ifmap::port"][::std::mem::offset_of!(ifmap, port) - 20usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct if_settings { + pub type_: ::std::os::raw::c_uint, + pub size: ::std::os::raw::c_uint, + pub ifs_ifsu: if_settings__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union if_settings__bindgen_ty_1 { + pub raw_hdlc: *mut raw_hdlc_proto, + pub cisco: *mut cisco_proto, + pub fr: *mut fr_proto, + pub fr_pvc: *mut fr_proto_pvc, + pub fr_pvc_info: *mut fr_proto_pvc_info, + pub x25: *mut x25_hdlc_proto, + pub sync: *mut sync_serial_settings, + pub te1: *mut te1_settings, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of if_settings__bindgen_ty_1"] + [::std::mem::size_of::() - 8usize]; + ["Alignment of if_settings__bindgen_ty_1"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: if_settings__bindgen_ty_1::raw_hdlc"] + [::std::mem::offset_of!(if_settings__bindgen_ty_1, raw_hdlc) - 0usize]; + ["Offset of field: if_settings__bindgen_ty_1::cisco"] + [::std::mem::offset_of!(if_settings__bindgen_ty_1, cisco) - 0usize]; + ["Offset of field: if_settings__bindgen_ty_1::fr"] + [::std::mem::offset_of!(if_settings__bindgen_ty_1, fr) - 0usize]; + ["Offset of field: if_settings__bindgen_ty_1::fr_pvc"] + [::std::mem::offset_of!(if_settings__bindgen_ty_1, fr_pvc) - 0usize]; + ["Offset of field: if_settings__bindgen_ty_1::fr_pvc_info"] + [::std::mem::offset_of!(if_settings__bindgen_ty_1, fr_pvc_info) - 0usize]; + ["Offset of field: if_settings__bindgen_ty_1::x25"] + [::std::mem::offset_of!(if_settings__bindgen_ty_1, x25) - 0usize]; + ["Offset of field: if_settings__bindgen_ty_1::sync"] + [::std::mem::offset_of!(if_settings__bindgen_ty_1, sync) - 0usize]; + ["Offset of field: if_settings__bindgen_ty_1::te1"] + [::std::mem::offset_of!(if_settings__bindgen_ty_1, te1) - 0usize]; +}; +impl Default for if_settings__bindgen_ty_1 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of if_settings"][::std::mem::size_of::() - 16usize]; + ["Alignment of if_settings"][::std::mem::align_of::() - 8usize]; + ["Offset of field: if_settings::type_"][::std::mem::offset_of!(if_settings, type_) - 0usize]; + ["Offset of field: if_settings::size"][::std::mem::offset_of!(if_settings, size) - 4usize]; + ["Offset of field: if_settings::ifs_ifsu"] + [::std::mem::offset_of!(if_settings, ifs_ifsu) - 8usize]; +}; +impl Default for if_settings { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct ifreq { + pub ifr_ifrn: ifreq__bindgen_ty_1, + pub ifr_ifru: ifreq__bindgen_ty_2, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union ifreq__bindgen_ty_1 { + pub ifrn_name: [::std::os::raw::c_uchar; 16usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ifreq__bindgen_ty_1"][::std::mem::size_of::() - 16usize]; + ["Alignment of ifreq__bindgen_ty_1"][::std::mem::align_of::() - 1usize]; + ["Offset of field: ifreq__bindgen_ty_1::ifrn_name"] + [::std::mem::offset_of!(ifreq__bindgen_ty_1, ifrn_name) - 0usize]; +}; +impl Default for ifreq__bindgen_ty_1 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union ifreq__bindgen_ty_2 { + pub ifru_addr: sockaddr, + pub ifru_dstaddr: sockaddr, + pub ifru_broadaddr: sockaddr, + pub ifru_netmask: sockaddr, + pub ifru_hwaddr: sockaddr, + pub ifru_flags: ::std::os::raw::c_short, + pub ifru_ivalue: ::std::os::raw::c_int, + pub ifru_mtu: ::std::os::raw::c_int, + pub ifru_map: ifmap, + pub ifru_slave: [::std::os::raw::c_char; 16usize], + pub ifru_newname: [::std::os::raw::c_char; 16usize], + pub ifru_data: *mut ::std::os::raw::c_void, + pub ifru_settings: if_settings, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ifreq__bindgen_ty_2"][::std::mem::size_of::() - 24usize]; + ["Alignment of ifreq__bindgen_ty_2"][::std::mem::align_of::() - 8usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_addr"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_addr) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_dstaddr"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_dstaddr) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_broadaddr"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_broadaddr) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_netmask"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_netmask) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_hwaddr"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_hwaddr) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_flags"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_flags) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_ivalue"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_ivalue) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_mtu"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_mtu) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_map"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_map) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_slave"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_slave) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_newname"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_newname) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_data"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_data) - 0usize]; + ["Offset of field: ifreq__bindgen_ty_2::ifru_settings"] + [::std::mem::offset_of!(ifreq__bindgen_ty_2, ifru_settings) - 0usize]; +}; +impl Default for ifreq__bindgen_ty_2 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ifreq"][::std::mem::size_of::() - 40usize]; + ["Alignment of ifreq"][::std::mem::align_of::() - 8usize]; + ["Offset of field: ifreq::ifr_ifrn"][::std::mem::offset_of!(ifreq, ifr_ifrn) - 0usize]; + ["Offset of field: ifreq::ifr_ifru"][::std::mem::offset_of!(ifreq, ifr_ifru) - 16usize]; +}; +impl Default for ifreq { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct ifconf { + pub ifc_len: ::std::os::raw::c_int, + pub ifc_ifcu: ifconf__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union ifconf__bindgen_ty_1 { + pub ifcu_buf: *mut ::std::os::raw::c_char, + pub ifcu_req: *mut ifreq, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ifconf__bindgen_ty_1"][::std::mem::size_of::() - 8usize]; + ["Alignment of ifconf__bindgen_ty_1"][::std::mem::align_of::() - 8usize]; + ["Offset of field: ifconf__bindgen_ty_1::ifcu_buf"] + [::std::mem::offset_of!(ifconf__bindgen_ty_1, ifcu_buf) - 0usize]; + ["Offset of field: ifconf__bindgen_ty_1::ifcu_req"] + [::std::mem::offset_of!(ifconf__bindgen_ty_1, ifcu_req) - 0usize]; +}; +impl Default for ifconf__bindgen_ty_1 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of ifconf"][::std::mem::size_of::() - 16usize]; + ["Alignment of ifconf"][::std::mem::align_of::() - 8usize]; + ["Offset of field: ifconf::ifc_len"][::std::mem::offset_of!(ifconf, ifc_len) - 0usize]; + ["Offset of field: ifconf::ifc_ifcu"][::std::mem::offset_of!(ifconf, ifc_ifcu) - 8usize]; +}; +impl Default for ifconf { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} diff --git a/src/vmm/src/devices/virtio/net/gen/mod.rs b/src/vmm/src/devices/virtio/net/generated/mod.rs similarity index 100% rename from src/vmm/src/devices/virtio/net/gen/mod.rs rename to src/vmm/src/devices/virtio/net/generated/mod.rs diff --git a/src/vmm/src/devices/virtio/net/gen/sockios.rs b/src/vmm/src/devices/virtio/net/generated/sockios.rs similarity index 96% rename from src/vmm/src/devices/virtio/net/gen/sockios.rs rename to src/vmm/src/devices/virtio/net/generated/sockios.rs index d1ffd6af27c..8973c1cf877 100644 --- a/src/vmm/src/devices/virtio/net/gen/sockios.rs +++ b/src/vmm/src/devices/virtio/net/generated/sockios.rs @@ -1,4 +1,4 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // automatically generated by tools/bindgen.sh @@ -11,7 +11,8 @@ clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] pub const __BITS_PER_LONG: u32 = 64; diff --git a/src/vmm/src/devices/virtio/net/metrics.rs b/src/vmm/src/devices/virtio/net/metrics.rs index dfdf69702a7..60e03f224de 100644 --- a/src/vmm/src/devices/virtio/net/metrics.rs +++ b/src/vmm/src/devices/virtio/net/metrics.rs @@ -75,7 +75,8 @@ //! //! The system implements 1 types of metrics: //! * Shared Incremental Metrics (SharedIncMetrics) - dedicated for the metrics which need a counter -//! (i.e the number of times an API request failed). These metrics are reset upon flush. +//! (i.e the number of times an API request failed). These metrics are reset upon flush. +//! //! We use net::metrics::METRICS instead of adding an entry of NetDeviceMetrics //! in Net so that metrics are accessible to be flushed even from signal handlers. diff --git a/src/vmm/src/devices/virtio/net/mod.rs b/src/vmm/src/devices/virtio/net/mod.rs index 197f2069d47..31b0f8a178c 100644 --- a/src/vmm/src/devices/virtio/net/mod.rs +++ b/src/vmm/src/devices/virtio/net/mod.rs @@ -6,7 +6,7 @@ use std::io; /// Maximum size of the queue for network device. -pub const NET_QUEUE_MAX_SIZE: u16 = 512; +pub const NET_QUEUE_MAX_SIZE: u16 = 256; /// Maximum size of the frame buffers handled by this device. pub const MAX_BUFFER_SIZE: usize = 65562; /// The number of queues of the network device. @@ -24,7 +24,7 @@ pub mod persist; mod tap; pub mod test_utils; -mod gen; +mod generated; pub use tap::{Tap, TapError}; use vm_memory::VolatileMemoryError; diff --git a/src/vmm/src/devices/virtio/net/persist.rs b/src/vmm/src/devices/virtio/net/persist.rs index fb62dcb0abe..5f2d6f560b4 100644 --- a/src/vmm/src/devices/virtio/net/persist.rs +++ b/src/vmm/src/devices/virtio/net/persist.rs @@ -10,15 +10,15 @@ use std::sync::{Arc, Mutex}; use serde::{Deserialize, Serialize}; use super::device::{Net, RxBuffers}; -use super::{TapError, NET_NUM_QUEUES, NET_QUEUE_MAX_SIZE, RX_INDEX}; +use super::{NET_NUM_QUEUES, NET_QUEUE_MAX_SIZE, RX_INDEX, TapError}; +use crate::devices::virtio::TYPE_NET; use crate::devices::virtio::device::DeviceState; use crate::devices::virtio::persist::{PersistError as VirtioStateError, VirtioDeviceState}; -use crate::devices::virtio::TYPE_NET; use crate::mmds::data_store::Mmds; use crate::mmds::ns::MmdsNetworkStack; use crate::mmds::persist::MmdsNetworkStackState; -use crate::rate_limiter::persist::RateLimiterState; use crate::rate_limiter::RateLimiter; +use crate::rate_limiter::persist::RateLimiterState; use crate::snapshot::Persist; use crate::utils::net::mac::MacAddr; use crate::vstate::memory::GuestMemoryMmap; @@ -55,8 +55,8 @@ impl RxBufferState { /// at snapshot. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetState { - id: String, - tap_if_name: String, + pub id: String, + pub tap_if_name: String, rx_rate_limiter_state: RateLimiterState, tx_rate_limiter_state: RateLimiterState, /// The associated MMDS network stack. diff --git a/src/vmm/src/devices/virtio/net/tap.rs b/src/vmm/src/devices/virtio/net/tap.rs index 32c665e1c31..c516705af31 100644 --- a/src/vmm/src/devices/virtio/net/tap.rs +++ b/src/vmm/src/devices/virtio/net/tap.rs @@ -15,7 +15,7 @@ use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr}; use crate::devices::virtio::iovec::IoVecBuffer; -use crate::devices::virtio::net::gen; +use crate::devices::virtio::net::generated; // As defined in the Linux UAPI: // https://elixir.bootlin.com/linux/v4.17/source/include/uapi/linux/if.h#L33 @@ -71,7 +71,7 @@ fn build_terminated_if_name(if_name: &str) -> Result<[u8; IFACE_NAME_MAX_LEN], T } #[derive(Copy, Clone)] -pub struct IfReqBuilder(gen::ifreq); +pub struct IfReqBuilder(generated::ifreq); impl fmt::Debug for IfReqBuilder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -102,7 +102,7 @@ impl IfReqBuilder { mut self, socket: &F, ioctl: u64, - ) -> std::io::Result { + ) -> std::io::Result { // SAFETY: ioctl is safe. Called with a valid socket fd, and we check the return. if unsafe { ioctl_with_mut_ref(socket, ioctl, &mut self.0) } < 0 { return Err(IoError::last_os_error()); @@ -122,7 +122,7 @@ impl Tap { // string and verify the result. let fd = unsafe { libc::open( - b"/dev/net/tun\0".as_ptr().cast::(), + c"/dev/net/tun".as_ptr(), libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC, ) }; @@ -136,7 +136,10 @@ impl Tap { let terminated_if_name = build_terminated_if_name(if_name)?; let ifreq = IfReqBuilder::new() .if_name(&terminated_if_name) - .flags(i16::try_from(gen::IFF_TAP | gen::IFF_NO_PI | gen::IFF_VNET_HDR).unwrap()) + .flags( + i16::try_from(generated::IFF_TAP | generated::IFF_NO_PI | generated::IFF_VNET_HDR) + .unwrap(), + ) .execute(&tuntap, TUNSETIFF()) .map_err(|io_error| TapError::IfreqExecuteError(io_error, if_name.to_owned()))?; @@ -219,8 +222,8 @@ pub mod tests { use std::os::unix::ffi::OsStrExt; use super::*; - use crate::devices::virtio::net::gen; - use crate::devices::virtio::net::test_utils::{enable, if_index, TapTrafficSimulator}; + use crate::devices::virtio::net::generated; + use crate::devices::virtio::net::test_utils::{TapTrafficSimulator, enable, if_index}; // Redefine `IoVecBufferMut` with specific length. Otherwise // Rust will not know what to do. @@ -235,7 +238,7 @@ pub mod tests { fn test_tap_name() { // Sanity check that the assumed max iface name length is correct. assert_eq!(IFACE_NAME_MAX_LEN, unsafe { - gen::ifreq__bindgen_ty_1::default().ifrn_name.len() + generated::ifreq__bindgen_ty_1::default().ifrn_name.len() }); // Empty name - The tap should be named "tap0" by default @@ -277,19 +280,6 @@ pub mod tests { let tap = Tap::open_named("").unwrap(); tap.set_vnet_hdr_size(16).unwrap(); tap.set_offload(0).unwrap(); - - let faulty_tap = Tap { - tap_file: unsafe { File::from_raw_fd(-2) }, - if_name: [0x01; 16], - }; - assert_eq!( - faulty_tap.set_vnet_hdr_size(16).unwrap_err().to_string(), - TapError::SetSizeOfVnetHdr(IoError::from_raw_os_error(9)).to_string() - ); - assert_eq!( - faulty_tap.set_offload(0).unwrap_err().to_string(), - TapError::SetOffloadFlags(IoError::from_raw_os_error(9)).to_string() - ); } #[test] @@ -305,8 +295,8 @@ pub mod tests { let tap_traffic_simulator = TapTrafficSimulator::new(if_index(&tap)); let mut fragment1 = vmm_sys_util::rand::rand_bytes(PAYLOAD_SIZE); - fragment1.as_mut_slice()[..gen::ETH_HLEN as usize] - .copy_from_slice(&[0; gen::ETH_HLEN as usize]); + fragment1.as_mut_slice()[..generated::ETH_HLEN as usize] + .copy_from_slice(&[0; generated::ETH_HLEN as usize]); let fragment2 = vmm_sys_util::rand::rand_bytes(PAYLOAD_SIZE); let fragment3 = vmm_sys_util::rand::rand_bytes(PAYLOAD_SIZE); diff --git a/src/vmm/src/devices/virtio/net/test_utils.rs b/src/vmm/src/devices/virtio/net/test_utils.rs index ffe7bbc7279..5762123be68 100644 --- a/src/vmm/src/devices/virtio/net/test_utils.rs +++ b/src/vmm/src/devices/virtio/net/test_utils.rs @@ -12,13 +12,13 @@ use std::str::FromStr; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use crate::devices::DeviceError; +use crate::devices::virtio::net::Net; #[cfg(test)] use crate::devices::virtio::net::device::vnet_hdr_len; use crate::devices::virtio::net::tap::{IfReqBuilder, Tap}; -use crate::devices::virtio::net::Net; use crate::devices::virtio::queue::{Queue, QueueError}; use crate::devices::virtio::test_utils::VirtQueue; -use crate::devices::DeviceError; use crate::mmds::data_store::Mmds; use crate::mmds::ns::MmdsNetworkStack; use crate::rate_limiter::RateLimiter; @@ -211,7 +211,10 @@ pub fn if_index(tap: &Tap) -> i32 { let sock = create_socket(); let ifreq = IfReqBuilder::new() .if_name(&tap.if_name) - .execute(&sock, c_ulong::from(super::gen::sockios::SIOCGIFINDEX)) + .execute( + &sock, + c_ulong::from(super::generated::sockios::SIOCGIFINDEX), + ) .unwrap(); // SAFETY: Using this union variant is safe since `SIOCGIFINDEX` returns an integer. @@ -234,13 +237,16 @@ pub fn enable(tap: &Tap) { IfReqBuilder::new() .if_name(&tap.if_name) .flags( - (crate::devices::virtio::net::gen::net_device_flags_IFF_UP - | crate::devices::virtio::net::gen::net_device_flags_IFF_RUNNING - | crate::devices::virtio::net::gen::net_device_flags_IFF_NOARP) + (crate::devices::virtio::net::generated::net_device_flags_IFF_UP + | crate::devices::virtio::net::generated::net_device_flags_IFF_RUNNING + | crate::devices::virtio::net::generated::net_device_flags_IFF_NOARP) .try_into() .unwrap(), ) - .execute(&sock, c_ulong::from(super::gen::sockios::SIOCSIFFLAGS)) + .execute( + &sock, + c_ulong::from(super::generated::sockios::SIOCSIFFLAGS), + ) .unwrap(); } @@ -307,11 +313,11 @@ pub mod test { use crate::check_metric_after_block; use crate::devices::virtio::device::{IrqType, VirtioDevice}; use crate::devices::virtio::net::device::vnet_hdr_len; - use crate::devices::virtio::net::gen::ETH_HLEN; + use crate::devices::virtio::net::generated::ETH_HLEN; use crate::devices::virtio::net::test_utils::{ - assign_queues, default_net, inject_tap_tx_frame, NetEvent, NetQueue, + NetEvent, NetQueue, assign_queues, default_net, inject_tap_tx_frame, }; - use crate::devices::virtio::net::{Net, MAX_BUFFER_SIZE, RX_INDEX, TX_INDEX}; + use crate::devices::virtio::net::{MAX_BUFFER_SIZE, Net, RX_INDEX, TX_INDEX}; use crate::devices::virtio::queue::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE}; use crate::devices::virtio::test_utils::{VirtQueue, VirtqDesc}; use crate::logger::IncMetric; diff --git a/src/vmm/src/devices/virtio/persist.rs b/src/vmm/src/devices/virtio/persist.rs index 23293a25eab..7c861352317 100644 --- a/src/vmm/src/devices/virtio/persist.rs +++ b/src/vmm/src/devices/virtio/persist.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use super::queue::QueueError; use crate::devices::virtio::device::VirtioDevice; -use crate::devices::virtio::gen::virtio_ring::VIRTIO_RING_F_EVENT_IDX; +use crate::devices::virtio::generated::virtio_ring::VIRTIO_RING_F_EVENT_IDX; use crate::devices::virtio::mmio::MmioTransport; use crate::devices::virtio::queue::Queue; use crate::snapshot::Persist; @@ -22,7 +22,7 @@ use crate::vstate::memory::{GuestAddress, GuestMemoryMmap}; pub enum PersistError { /// Snapshot state contains invalid queue info. InvalidInput, - /// Could not restore queue. + /// Could not restore queue: {0} QueueConstruction(QueueError), } @@ -160,7 +160,7 @@ impl VirtioDeviceState { return Err(PersistError::InvalidInput); } - let uses_notif_suppression = (self.acked_features & 1u64 << VIRTIO_RING_F_EVENT_IDX) != 0; + let uses_notif_suppression = (self.acked_features & (1u64 << VIRTIO_RING_F_EVENT_IDX)) != 0; let queue_construction_args = QueueConstructorArgs { mem: mem.clone(), is_activated: self.activated, @@ -258,12 +258,12 @@ mod tests { use vmm_sys_util::tempfile::TempFile; use super::*; + use crate::devices::virtio::block::virtio::VirtioBlock; use crate::devices::virtio::block::virtio::device::FileEngineType; use crate::devices::virtio::block::virtio::test_utils::default_block_with_path; - use crate::devices::virtio::block::virtio::VirtioBlock; use crate::devices::virtio::mmio::tests::DummyDevice; - use crate::devices::virtio::net::test_utils::default_net; use crate::devices::virtio::net::Net; + use crate::devices::virtio::net::test_utils::default_net; use crate::devices::virtio::test_utils::default_mem; use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend}; use crate::snapshot::Snapshot; diff --git a/src/vmm/src/devices/virtio/queue.rs b/src/vmm/src/devices/virtio/queue.rs index 39ccef1f1ee..efe42bfc3dc 100644 --- a/src/vmm/src/devices/virtio/queue.rs +++ b/src/vmm/src/devices/virtio/queue.rs @@ -7,7 +7,7 @@ use std::cmp::min; use std::num::Wrapping; -use std::sync::atomic::{fence, Ordering}; +use std::sync::atomic::{Ordering, fence}; use crate::logger::error; use crate::vstate::memory::{Address, Bitmap, ByteValued, GuestAddress, GuestMemory}; @@ -34,6 +34,8 @@ pub enum QueueError { DescIndexOutOfBounds(u16), /// Failed to write value into the virtio queue used ring: {0} MemoryError(#[from] vm_memory::GuestMemoryError), + /// Pointer is not aligned properly: {0:#x} not {1}-byte aligned. + PointerNotAligned(usize, u8), } /// A virtio descriptor constraints with C representative. @@ -114,11 +116,7 @@ impl DescriptorChain { next: desc.next, }; - if chain.is_valid() { - Some(chain) - } else { - None - } + if chain.is_valid() { Some(chain) } else { None } } fn is_valid(&self) -> bool { @@ -172,9 +170,8 @@ impl Iterator for DescriptorIterator { type Item = DescriptorChain; fn next(&mut self) -> Option { - self.0.take().map(|desc| { + self.0.take().inspect(|desc| { self.0 = desc.next_descriptor(); - desc }) } } @@ -324,11 +321,36 @@ impl Queue { .get_slice_ptr(mem, self.used_ring_address, self.used_ring_size())? .cast(); - // Disable it for kani tests, otherwise it will hit this assertion - // and fail. - #[cfg(not(kani))] - if self.actual_size() < self.len() { - return Err(QueueError::InvalidQueueSize(self.len(), self.actual_size())); + // All the above pointers are expected to be aligned properly; otherwise some methods (e.g. + // `read_volatile()`) will panic. Such an unalignment is possible when restored from a + // broken/fuzzed snapshot. + // + // Specification of those pointers' alignments + // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-350007 + // > ================ ========== + // > Virtqueue Part Alignment + // > ================ ========== + // > Descriptor Table 16 + // > Available Ring 2 + // > Used Ring 4 + // > ================ ========== + if !self.desc_table_ptr.cast::().is_aligned() { + return Err(QueueError::PointerNotAligned( + self.desc_table_ptr as usize, + 16, + )); + } + if !self.avail_ring_ptr.is_aligned() { + return Err(QueueError::PointerNotAligned( + self.avail_ring_ptr as usize, + 2, + )); + } + if !self.used_ring_ptr.cast::().is_aligned() { + return Err(QueueError::PointerNotAligned( + self.used_ring_ptr as usize, + 4, + )); } Ok(()) @@ -560,10 +582,9 @@ impl Queue { // index is bound by the queue size let desc_index = unsafe { self.avail_ring_ring_get(usize::from(idx)) }; - DescriptorChain::checked_new(self.desc_table_ptr, self.actual_size(), desc_index).map( - |dc| { + DescriptorChain::checked_new(self.desc_table_ptr, self.actual_size(), desc_index).inspect( + |_| { self.next_avail += Wrapping(1); - dc }, ) } @@ -575,9 +596,8 @@ impl Queue { } /// Write used element into used_ring ring. - /// - [`ring_index_offset`] is an offset added to - /// the current [`self.next_used`] to obtain actual index - /// into used_ring. + /// - [`ring_index_offset`] is an offset added to the current [`self.next_used`] to obtain + /// actual index into used_ring. pub fn write_used_element( &mut self, ring_index_offset: u16, @@ -779,10 +799,11 @@ mod verification { const GUEST_MEMORY_BASE: u64 = 512; // We size our guest memory to fit a properly aligned queue, plus some wiggles bytes - // to make sure we not only test queues where all segments are consecutively aligned. + // to make sure we not only test queues where all segments are consecutively aligned (at least + // for those proofs that use a completely arbitrary queue structure). // We need to give at least 16 bytes of buffer space for the descriptor table to be // able to change its address, as it is 16-byte aligned. - const GUEST_MEMORY_SIZE: usize = QUEUE_END as usize + 30; + const GUEST_MEMORY_SIZE: usize = (QUEUE_END - QUEUE_BASE_ADDRESS) as usize + 30; fn guest_memory(memory: *mut u8) -> ProofGuestMemory { // Ideally, we'd want to do @@ -852,8 +873,7 @@ mod verification { const USED_RING_BASE_ADDRESS: u64 = AVAIL_RING_BASE_ADDRESS + 6 + 2 * FIRECRACKER_MAX_QUEUE_SIZE as u64 + 2; - /// The address of the first byte after the queue. Since our queue starts at guest physical - /// address 0, this is also the size of the memory area occupied by the queue. + /// The address of the first byte after the queue (which starts at QUEUE_BASE_ADDRESS). /// Note that the used ring structure has size 6 + 8 * FIRECRACKER_MAX_QUEUE_SIZE const QUEUE_END: u64 = USED_RING_BASE_ADDRESS + 6 + 8 * FIRECRACKER_MAX_QUEUE_SIZE as u64; @@ -931,8 +951,8 @@ mod verification { #[kani::proof] #[kani::unwind(0)] // There are no loops anywhere, but kani really enjoys getting stuck in std::ptr::drop_in_place. - // This is a compiler intrinsic that has a "dummy" implementation in stdlib that just - // recursively calls itself. Kani will generally unwind this recursion infinitely + // This is a compiler intrinsic that has a "dummy" implementation in stdlib that just + // recursively calls itself. Kani will generally unwind this recursion infinitely fn verify_spec_2_6_7_2() { // Section 2.6.7.2 deals with device-to-driver notification suppression. // It describes a mechanism by which the driver can tell the device that it does not @@ -1067,11 +1087,7 @@ mod verification { // Section 2.6: Alignment of descriptor table, available ring and used ring; size of // queue fn alignment_of(val: u64) -> u64 { - if val == 0 { - u64::MAX - } else { - val & (!val + 1) - } + if val == 0 { u64::MAX } else { val & (!val + 1) } } assert!(alignment_of(queue.desc_table_address.0) >= 16); @@ -1237,12 +1253,11 @@ mod verification { #[cfg(test)] mod tests { - use vm_memory::Bytes; pub use super::*; use crate::devices::virtio::queue::QueueError::DescIndexOutOfBounds; - use crate::devices::virtio::test_utils::{default_mem, VirtQueue}; + use crate::devices::virtio::test_utils::{VirtQueue, default_mem}; use crate::test_utils::{multi_region_mem, single_region_mem}; use crate::vstate::memory::GuestAddress; @@ -1647,6 +1662,63 @@ mod tests { assert_eq!(q.used_ring_avail_event_get(), 1); } + #[test] + fn test_initialize_with_aligned_pointer() { + let mut q = Queue::new(0); + + let random_addr = 0x321; + // Descriptor table must be 16-byte aligned. + q.desc_table_address = GuestAddress(random_addr / 16 * 16); + // Available ring must be 2-byte aligned. + q.avail_ring_address = GuestAddress(random_addr / 2 * 2); + // Used ring must be 4-byte aligned. + q.avail_ring_address = GuestAddress(random_addr / 4 * 4); + + let mem = single_region_mem(0x1000); + q.initialize(&mem).unwrap(); + } + + #[test] + fn test_initialize_with_misaligned_pointer() { + let mut q = Queue::new(0); + let mem = single_region_mem(0x1000); + + // Descriptor table must be 16-byte aligned. + q.desc_table_address = GuestAddress(0xb); + match q.initialize(&mem) { + Ok(_) => panic!("Unexpected success"), + Err(QueueError::PointerNotAligned(addr, alignment)) => { + assert_eq!(addr % 16, 0xb); + assert_eq!(alignment, 16); + } + Err(e) => panic!("Unexpected error {e:#?}"), + } + q.desc_table_address = GuestAddress(0x0); + + // Available ring must be 2-byte aligned. + q.avail_ring_address = GuestAddress(0x1); + match q.initialize(&mem) { + Ok(_) => panic!("Unexpected success"), + Err(QueueError::PointerNotAligned(addr, alignment)) => { + assert_eq!(addr % 2, 0x1); + assert_eq!(alignment, 2); + } + Err(e) => panic!("Unexpected error {e:#?}"), + } + q.avail_ring_address = GuestAddress(0x0); + + // Used ring must be 4-byte aligned. + q.used_ring_address = GuestAddress(0x3); + match q.initialize(&mem) { + Ok(_) => panic!("unexpected success"), + Err(QueueError::PointerNotAligned(addr, alignment)) => { + assert_eq!(addr % 4, 0x3); + assert_eq!(alignment, 4); + } + Err(e) => panic!("Unexpected error {e:#?}"), + } + } + #[test] fn test_queue_error_display() { let err = QueueError::MemoryError(vm_memory::GuestMemoryError::InvalidGuestAddress( diff --git a/src/vmm/src/devices/virtio/rng/device.rs b/src/vmm/src/devices/virtio/rng/device.rs index 96513e49b26..97ac8676e0a 100644 --- a/src/vmm/src/devices/virtio/rng/device.rs +++ b/src/vmm/src/devices/virtio/rng/device.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use std::io; -use std::sync::atomic::AtomicU32; use std::sync::Arc; +use std::sync::atomic::AtomicU32; use aws_lc_rs::rand; use vm_memory::GuestMemoryError; @@ -11,14 +11,14 @@ use vmm_sys_util::eventfd::EventFd; use super::metrics::METRICS; use super::{RNG_NUM_QUEUES, RNG_QUEUE}; +use crate::devices::DeviceError; use crate::devices::virtio::device::{DeviceState, IrqTrigger, IrqType, VirtioDevice}; -use crate::devices::virtio::gen::virtio_rng::VIRTIO_F_VERSION_1; +use crate::devices::virtio::generated::virtio_config::VIRTIO_F_VERSION_1; use crate::devices::virtio::iov_deque::IovDequeError; use crate::devices::virtio::iovec::IoVecBufferMut; -use crate::devices::virtio::queue::{Queue, FIRECRACKER_MAX_QUEUE_SIZE}; +use crate::devices::virtio::queue::{FIRECRACKER_MAX_QUEUE_SIZE, Queue}; use crate::devices::virtio::{ActivateError, TYPE_RNG}; -use crate::devices::DeviceError; -use crate::logger::{debug, error, IncMetric}; +use crate::logger::{IncMetric, debug, error}; use crate::rate_limiter::{RateLimiter, TokenType}; use crate::vstate::memory::GuestMemoryMmap; @@ -119,9 +119,8 @@ impl Entropy { } let mut rand_bytes = vec![0; self.buffer.len() as usize]; - rand::fill(&mut rand_bytes).map_err(|err| { + rand::fill(&mut rand_bytes).inspect_err(|_| { METRICS.host_rng_fails.inc(); - err })?; // It is ok to unwrap here. We are writing `iovec.len()` bytes at offset 0. @@ -315,7 +314,7 @@ mod tests { use crate::devices::virtio::device::VirtioDevice; use crate::devices::virtio::queue::VIRTQ_DESC_F_WRITE; use crate::devices::virtio::test_utils::test::{ - create_virtio_mem, VirtioTestDevice, VirtioTestHelper, + VirtioTestDevice, VirtioTestHelper, create_virtio_mem, }; impl VirtioTestDevice for Entropy { diff --git a/src/vmm/src/devices/virtio/rng/metrics.rs b/src/vmm/src/devices/virtio/rng/metrics.rs index eb1dcbfacae..e02f5ce8cc4 100644 --- a/src/vmm/src/devices/virtio/rng/metrics.rs +++ b/src/vmm/src/devices/virtio/rng/metrics.rs @@ -31,7 +31,7 @@ //! //! The system implements 1 type of metrics: //! * Shared Incremental Metrics (SharedIncMetrics) - dedicated for the metrics which need a counter -//! (i.e the number of times an API request failed). These metrics are reset upon flush. +//! (i.e the number of times an API request failed). These metrics are reset upon flush. use serde::ser::SerializeMap; use serde::{Serialize, Serializer}; diff --git a/src/vmm/src/devices/virtio/rng/persist.rs b/src/vmm/src/devices/virtio/rng/persist.rs index 4aa9e449344..2f2519b4962 100644 --- a/src/vmm/src/devices/virtio/rng/persist.rs +++ b/src/vmm/src/devices/virtio/rng/persist.rs @@ -5,12 +5,12 @@ use serde::{Deserialize, Serialize}; +use crate::devices::virtio::TYPE_RNG; use crate::devices::virtio::persist::{PersistError as VirtioStateError, VirtioDeviceState}; use crate::devices::virtio::queue::FIRECRACKER_MAX_QUEUE_SIZE; use crate::devices::virtio::rng::{Entropy, EntropyError, RNG_NUM_QUEUES}; -use crate::devices::virtio::TYPE_RNG; -use crate::rate_limiter::persist::RateLimiterState; use crate::rate_limiter::RateLimiter; +use crate::rate_limiter::persist::RateLimiterState; use crate::snapshot::Persist; use crate::vstate::memory::GuestMemoryMmap; diff --git a/src/vmm/src/devices/virtio/test_utils.rs b/src/vmm/src/devices/virtio/test_utils.rs index 9bb66db82ae..8642d0a85f4 100644 --- a/src/vmm/src/devices/virtio/test_utils.rs +++ b/src/vmm/src/devices/virtio/test_utils.rs @@ -10,7 +10,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use crate::devices::virtio::queue::Queue; use crate::test_utils::single_region_mem; -use crate::utils::u64_to_usize; +use crate::utils::{align_up, u64_to_usize}; use crate::vstate::memory::{Address, Bytes, GuestAddress, GuestMemoryMmap}; #[macro_export] @@ -250,7 +250,7 @@ impl<'a> VirtQueue<'a> { const USED_ALIGN: u64 = 4; let mut x = avail.end().0; - x = (x + USED_ALIGN - 1) & !(USED_ALIGN - 1); + x = align_up(x, USED_ALIGN); let used = VirtqUsed::new(GuestAddress(x), mem, qsize, u64_to_usize(USED_ALIGN)); @@ -377,7 +377,7 @@ pub(crate) mod test { const QUEUE_SIZE: u16 = 16; // Helper function to create a set of Virtqueues for the device - fn create_virtqueues(mem: &'a GuestMemoryMmap, num_queues: usize) -> Vec { + fn create_virtqueues(mem: &'a GuestMemoryMmap, num_queues: usize) -> Vec> { (0..num_queues) .scan(GuestAddress(0), |next_addr, _| { let vqueue = VirtQueue::new(*next_addr, mem, Self::QUEUE_SIZE); diff --git a/src/vmm/src/devices/virtio/vhost_user.rs b/src/vmm/src/devices/virtio/vhost_user.rs index ad86c9942af..83174fbc4d3 100644 --- a/src/vmm/src/devices/virtio/vhost_user.rs +++ b/src/vmm/src/devices/virtio/vhost_user.rs @@ -373,7 +373,7 @@ impl VhostUserHandleImpl { None => { return Err(VhostUserError::VhostUserMemoryRegion( MmapError::NoMemoryRegion, - )) + )); } }; @@ -459,14 +459,30 @@ impl VhostUserHandleImpl { } #[cfg(test)] -mod tests { +pub(crate) mod tests { #![allow(clippy::undocumented_unsafe_blocks)] + use std::fs::File; + use vmm_sys_util::tempfile::TempFile; use super::*; use crate::test_utils::create_tmp_socket; - use crate::vstate::memory::{FileOffset, GuestAddress, GuestMemoryExtension}; + use crate::vstate::memory; + use crate::vstate::memory::GuestAddress; + + pub(crate) fn create_mem(file: File, regions: &[(GuestAddress, usize)]) -> GuestMemoryMmap { + GuestMemoryMmap::from_regions( + memory::create( + regions.iter().copied(), + libc::MAP_PRIVATE, + Some(file), + false, + ) + .unwrap(), + ) + .unwrap() + } #[test] fn test_new() { @@ -759,19 +775,11 @@ mod tests { let file_size = 2 * region_size; file.set_len(file_size as u64).unwrap(); let regions = vec![ - ( - FileOffset::new(file.try_clone().unwrap(), 0x0), - GuestAddress(0x0), - region_size, - ), - ( - FileOffset::new(file.try_clone().unwrap(), 0x10000), - GuestAddress(0x10000), - region_size, - ), + (GuestAddress(0x0), region_size), + (GuestAddress(0x10000), region_size), ]; - let guest_memory = GuestMemoryMmap::from_raw_regions_file(regions, false, false).unwrap(); + let guest_memory = create_mem(file, ®ions); vuh.update_mem_table(&guest_memory).unwrap(); @@ -883,13 +891,9 @@ mod tests { let region_size = 0x10000; let file = TempFile::new().unwrap().into_file(); file.set_len(region_size as u64).unwrap(); - let regions = vec![( - FileOffset::new(file.try_clone().unwrap(), 0x0), - GuestAddress(0x0), - region_size, - )]; + let regions = vec![(GuestAddress(0x0), region_size)]; - let guest_memory = GuestMemoryMmap::from_raw_regions_file(regions, false, false).unwrap(); + let guest_memory = create_mem(file, ®ions); let mut queue = Queue::new(69); queue.initialize(&guest_memory).unwrap(); diff --git a/src/vmm/src/devices/virtio/vhost_user_metrics.rs b/src/vmm/src/devices/virtio/vhost_user_metrics.rs index 73b41e6a86c..bb7f03b30a6 100644 --- a/src/vmm/src/devices/virtio/vhost_user_metrics.rs +++ b/src/vmm/src/devices/virtio/vhost_user_metrics.rs @@ -64,9 +64,10 @@ //! //! The system implements 2 type of metrics: //! * Shared Incremental Metrics (SharedIncMetrics) - dedicated for the metrics which need a counter -//! (i.e the number of times activating a device failed). These metrics are reset upon flush. +//! (i.e the number of times activating a device failed). These metrics are reset upon flush. //! * Shared Store Metrics (SharedStoreMetrics) - are targeted at keeping a persistent value, it is //! `not` intended to act as a counter (i.e for measure the process start up time for example). +//! //! We add VhostUserDeviceMetrics entries from vhost_user_metrics::METRICS into vhost_user device //! instead of vhost_user device having individual separate VhostUserDeviceMetrics entries because //! vhost_user device is not accessible from signal handlers to flush metrics and @@ -143,7 +144,7 @@ pub struct VhostUserDeviceMetrics { #[cfg(test)] pub mod tests { - use utils::time::{get_time_us, ClockType}; + use utils::time::{ClockType, get_time_us}; use super::*; use crate::logger::{IncMetric, StoreMetric}; diff --git a/src/vmm/src/devices/virtio/vsock/csm/connection.rs b/src/vmm/src/devices/virtio/vsock/csm/connection.rs index ad2340413f6..c9bd5b2c0f7 100644 --- a/src/vmm/src/devices/virtio/vsock/csm/connection.rs +++ b/src/vmm/src/devices/virtio/vsock/csm/connection.rs @@ -83,14 +83,14 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::time::{Duration, Instant}; use log::{debug, error, info, warn}; -use vm_memory::io::{ReadVolatile, WriteVolatile}; use vm_memory::GuestMemoryError; +use vm_memory::io::{ReadVolatile, WriteVolatile}; use vmm_sys_util::epoll::EventSet; use super::super::defs::uapi; use super::super::{VsockChannel, VsockEpollListener, VsockError}; use super::txbuf::TxBuf; -use super::{defs, ConnState, PendingRx, PendingRxSet, VsockCsmError}; +use super::{ConnState, PendingRx, PendingRxSet, VsockCsmError, defs}; use crate::devices::virtio::vsock::metrics::METRICS; use crate::devices::virtio::vsock::packet::{VsockPacketHeader, VsockPacketRx, VsockPacketTx}; use crate::logger::IncMetric; diff --git a/src/vmm/src/devices/virtio/vsock/csm/txbuf.rs b/src/vmm/src/devices/virtio/vsock/csm/txbuf.rs index 1742b02394f..d507491e8a0 100644 --- a/src/vmm/src/devices/virtio/vsock/csm/txbuf.rs +++ b/src/vmm/src/devices/virtio/vsock/csm/txbuf.rs @@ -8,7 +8,7 @@ use std::num::Wrapping; use vm_memory::{VolatileMemoryError, VolatileSlice, WriteVolatile}; -use super::{defs, VsockCsmError}; +use super::{VsockCsmError, defs}; use crate::utils::wrap_usize_to_u32; use crate::vstate::memory::{BitmapSlice, Bytes}; diff --git a/src/vmm/src/devices/virtio/vsock/device.rs b/src/vmm/src/devices/virtio/vsock/device.rs index bf438aca99f..aa114f6cccb 100644 --- a/src/vmm/src/devices/virtio/vsock/device.rs +++ b/src/vmm/src/devices/virtio/vsock/device.rs @@ -27,13 +27,14 @@ use vmm_sys_util::eventfd::EventFd; use super::super::super::DeviceError; use super::defs::uapi; -use super::packet::{VsockPacketRx, VsockPacketTx, VSOCK_PKT_HDR_SIZE}; -use super::{defs, VsockBackend}; +use super::packet::{VSOCK_PKT_HDR_SIZE, VsockPacketRx, VsockPacketTx}; +use super::{VsockBackend, defs}; +use crate::devices::virtio::ActivateError; use crate::devices::virtio::device::{DeviceState, IrqTrigger, IrqType, VirtioDevice}; +use crate::devices::virtio::generated::virtio_config::{VIRTIO_F_IN_ORDER, VIRTIO_F_VERSION_1}; use crate::devices::virtio::queue::Queue as VirtQueue; -use crate::devices::virtio::vsock::metrics::METRICS; use crate::devices::virtio::vsock::VsockError; -use crate::devices::virtio::ActivateError; +use crate::devices::virtio::vsock::metrics::METRICS; use crate::logger::IncMetric; use crate::utils::byte_order; use crate::vstate::memory::{Bytes, GuestMemoryMmap}; @@ -49,7 +50,7 @@ pub(crate) const VIRTIO_VSOCK_EVENT_TRANSPORT_RESET: u32 = 0; /// - VIRTIO_F_IN_ORDER: the device returns used buffers in the same order that the driver makes /// them available. pub(crate) const AVAIL_FEATURES: u64 = - 1 << uapi::VIRTIO_F_VERSION_1 as u64 | 1 << uapi::VIRTIO_F_IN_ORDER as u64; + (1 << VIRTIO_F_VERSION_1 as u64) | (1 << VIRTIO_F_IN_ORDER as u64); /// Structure representing the vsock device. #[derive(Debug)] @@ -322,7 +323,7 @@ where fn write_config(&mut self, offset: u64, data: &[u8]) { METRICS.cfg_fails.inc(); warn!( - "vsock: guest driver attempted to write device config (offset={:x}, len={:x})", + "vsock: guest driver attempted to write device config (offset={:#x}, len={:#x})", offset, data.len() ); diff --git a/src/vmm/src/devices/virtio/vsock/event_handler.rs b/src/vmm/src/devices/virtio/vsock/event_handler.rs index 40e75d1a9f5..632148546e5 100755 --- a/src/vmm/src/devices/virtio/vsock/event_handler.rs +++ b/src/vmm/src/devices/virtio/vsock/event_handler.rs @@ -9,7 +9,7 @@ use std::fmt::Debug; /// The vsock object implements the runtime logic of our vsock device: /// 1. Respond to TX queue events by wrapping virtio buffers into `VsockPacket`s, then sending -/// those packets to the `VsockBackend`; +/// those packets to the `VsockBackend`; /// 2. Forward backend FD event notifications to the `VsockBackend`; /// 3. Fetch incoming packets from the `VsockBackend` and place them into the virtio RX queue; /// 4. Whenever we have processed some virtio buffers (either TX or RX), let the driver know by @@ -30,8 +30,8 @@ use event_manager::{EventOps, Events, MutEventSubscriber}; use log::{error, warn}; use vmm_sys_util::epoll::EventSet; -use super::device::{Vsock, EVQ_INDEX, RXQ_INDEX, TXQ_INDEX}; use super::VsockBackend; +use super::device::{EVQ_INDEX, RXQ_INDEX, TXQ_INDEX, Vsock}; use crate::devices::virtio::device::VirtioDevice; use crate::devices::virtio::vsock::metrics::METRICS; use crate::logger::IncMetric; @@ -223,10 +223,7 @@ mod tests { use super::super::*; use super::*; - use crate::devices::virtio::vsock::packet::VSOCK_PKT_HDR_SIZE; use crate::devices::virtio::vsock::test_utils::{EventHandlerContext, TestContext}; - use crate::test_utils::multi_region_mem; - use crate::vstate::memory::Bytes; #[test] fn test_txq_event() { @@ -427,8 +424,9 @@ mod tests { // function for testing error cases, so the asserts always expect is_err() to be true. When // desc_idx = 0 we are altering the header (first descriptor in the chain), and when // desc_idx = 1 we are altering the packet buffer. + #[cfg(target_arch = "x86_64")] fn vsock_bof_helper(test_ctx: &mut TestContext, desc_idx: usize, addr: u64, len: u32) { - use crate::vstate::memory::GuestAddress; + use crate::vstate::memory::{Bytes, GuestAddress}; assert!(desc_idx <= 1); @@ -472,19 +470,23 @@ mod tests { } #[test] + #[cfg(target_arch = "x86_64")] + #[allow(clippy::cast_possible_truncation)] /* casting of constants we know fit into u32 */ fn test_vsock_bof() { + use crate::arch::MMIO_MEM_START; + use crate::arch::x86_64::{FIRST_ADDR_PAST_32BITS, MEM_32BIT_GAP_SIZE}; + use crate::devices::virtio::vsock::packet::VSOCK_PKT_HDR_SIZE; + use crate::test_utils::multi_region_mem; + use crate::utils::mib_to_bytes; use crate::vstate::memory::GuestAddress; - const GAP_SIZE: u32 = 768 << 20; - const FIRST_AFTER_GAP: usize = 1 << 32; - const GAP_START_ADDR: usize = FIRST_AFTER_GAP - GAP_SIZE as usize; - const MIB: usize = 1 << 20; + const MIB: usize = mib_to_bytes(1); let mut test_ctx = TestContext::new(); test_ctx.mem = multi_region_mem(&[ (GuestAddress(0), 8 * MIB), - (GuestAddress((GAP_START_ADDR - MIB) as u64), MIB), - (GuestAddress(FIRST_AFTER_GAP as u64), MIB), + (GuestAddress(MMIO_MEM_START - MIB as u64), MIB), + (GuestAddress(FIRST_ADDR_PAST_32BITS), MIB), ]); // The default configured descriptor chains are valid. @@ -506,20 +508,25 @@ mod tests { } // Let's check what happens when the header descriptor is right before the gap. - vsock_bof_helper( - &mut test_ctx, - 0, - GAP_START_ADDR as u64 - 1, - VSOCK_PKT_HDR_SIZE, - ); + vsock_bof_helper(&mut test_ctx, 0, MMIO_MEM_START - 1, VSOCK_PKT_HDR_SIZE); // Let's check what happens when the buffer descriptor crosses into the gap, but does // not go past its right edge. - vsock_bof_helper(&mut test_ctx, 1, GAP_START_ADDR as u64 - 4, GAP_SIZE + 4); + vsock_bof_helper( + &mut test_ctx, + 1, + MMIO_MEM_START - 4, + MEM_32BIT_GAP_SIZE as u32 + 4, + ); // Let's modify the buffer descriptor addr and len such that it crosses over the MMIO gap, // and check we cannot assemble the VsockPkts. - vsock_bof_helper(&mut test_ctx, 1, GAP_START_ADDR as u64 - 4, GAP_SIZE + 100); + vsock_bof_helper( + &mut test_ctx, + 1, + MMIO_MEM_START - 4, + MEM_32BIT_GAP_SIZE as u32 + 100, + ); } #[test] diff --git a/src/vmm/src/devices/virtio/vsock/metrics.rs b/src/vmm/src/devices/virtio/vsock/metrics.rs index 5307302f5ca..d626d5dca34 100644 --- a/src/vmm/src/devices/virtio/vsock/metrics.rs +++ b/src/vmm/src/devices/virtio/vsock/metrics.rs @@ -34,7 +34,7 @@ //! //! The system implements 1 type of metrics: //! * Shared Incremental Metrics (SharedIncMetrics) - dedicated for the metrics which need a counter -//! (i.e the number of times an API request failed). These metrics are reset upon flush. +//! (i.e the number of times an API request failed). These metrics are reset upon flush. use serde::ser::SerializeMap; use serde::{Serialize, Serializer}; diff --git a/src/vmm/src/devices/virtio/vsock/mod.rs b/src/vmm/src/devices/virtio/vsock/mod.rs index 8ffaaa8db0d..859e198860b 100644 --- a/src/vmm/src/devices/virtio/vsock/mod.rs +++ b/src/vmm/src/devices/virtio/vsock/mod.rs @@ -25,8 +25,8 @@ use std::os::unix::io::AsRawFd; use vm_memory::GuestMemoryError; use vmm_sys_util::epoll::EventSet; -pub use self::defs::uapi::VIRTIO_ID_VSOCK as TYPE_VSOCK; pub use self::defs::VSOCK_DEV_ID; +pub use self::defs::uapi::VIRTIO_ID_VSOCK as TYPE_VSOCK; pub use self::device::Vsock; use self::packet::{VsockPacketRx, VsockPacketTx}; pub use self::unix::{VsockUnixBackend, VsockUnixBackendError}; @@ -57,15 +57,6 @@ mod defs { pub mod uapi { - /// Virtio feature flags. - /// Defined in `/include/uapi/linux/virtio_config.h`. - /// - /// The device processes available buffers in the same order in which the device - /// offers them. - pub const VIRTIO_F_IN_ORDER: usize = 35; - /// The device conforms to the virtio spec version 1.0. - pub const VIRTIO_F_VERSION_1: u32 = 32; - /// Virtio vsock device ID. /// Defined in `include/uapi/linux/virtio_ids.h`. pub const VIRTIO_ID_VSOCK: u32 = 19; diff --git a/src/vmm/src/devices/virtio/vsock/packet.rs b/src/vmm/src/devices/virtio/vsock/packet.rs index 213b5bb2b34..78130af4b12 100644 --- a/src/vmm/src/devices/virtio/vsock/packet.rs +++ b/src/vmm/src/devices/virtio/vsock/packet.rs @@ -7,6 +7,7 @@ //! virtio queue: //! - the packet header; and //! - the packet data/buffer. +//! //! There is a 1:1 relation between descriptor chains and packets: the first (chain head) holds //! the header, and an optional second descriptor holds the data. The second descriptor is only //! present for data packets (VSOCK_OP_RW). @@ -20,7 +21,7 @@ use std::fmt::Debug; use vm_memory::volatile_memory::Error; use vm_memory::{GuestMemoryError, ReadVolatile, WriteVolatile}; -use super::{defs, VsockError}; +use super::{VsockError, defs}; use crate::devices::virtio::iovec::{IoVecBuffer, IoVecBufferMut}; use crate::devices::virtio::queue::DescriptorChain; use crate::vstate::memory::{ByteValued, GuestMemoryMmap}; @@ -44,7 +45,7 @@ use crate::vstate::memory::{ByteValued, GuestMemoryMmap}; // The mirroring struct is only used privately by `VsockPacket`, that offers getter and setter // methods, for each struct field, that will also handle the correct endianess. -#[repr(packed)] +#[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] pub struct VsockPacketHeader { // Source CID. @@ -222,7 +223,7 @@ impl VsockPacketTx { match self.buffer.read_exact_volatile_at(hdr.as_mut_slice(), 0) { Ok(()) => (), Err(Error::PartialBuffer { completed, .. }) => { - return Err(VsockError::DescChainTooShortForHeader(completed)) + return Err(VsockError::DescChainTooShortForHeader(completed)); } Err(err) => return Err(VsockError::GuestMemoryMmap(err.into())), } @@ -517,7 +518,7 @@ mod tests { )) } - // Test case: the buffer descriptor cannot fit all the data advertised by the the + // Test case: the buffer descriptor cannot fit all the data advertised by the // packet header `len` field. { create_context!(test_ctx, handler_ctx); diff --git a/src/vmm/src/devices/virtio/vsock/persist.rs b/src/vmm/src/devices/virtio/vsock/persist.rs index dce545fd68d..fce6affae69 100644 --- a/src/vmm/src/devices/virtio/vsock/persist.rs +++ b/src/vmm/src/devices/virtio/vsock/persist.rs @@ -4,8 +4,8 @@ //! Defines state and support structures for persisting Vsock devices and backends. use std::fmt::Debug; -use std::sync::atomic::AtomicU32; use std::sync::Arc; +use std::sync::atomic::AtomicU32; use serde::{Deserialize, Serialize}; diff --git a/src/vmm/src/devices/virtio/vsock/test_utils.rs b/src/vmm/src/devices/virtio/vsock/test_utils.rs index 4a5fdb2c941..804f0442559 100644 --- a/src/vmm/src/devices/virtio/vsock/test_utils.rs +++ b/src/vmm/src/devices/virtio/vsock/test_utils.rs @@ -190,7 +190,7 @@ pub struct EventHandlerContext<'a> { pub guest_evvq: GuestQ<'a>, } -impl<'a> EventHandlerContext<'a> { +impl EventHandlerContext<'_> { pub fn mock_activate(&mut self, mem: GuestMemoryMmap) { // Artificially activate the device. self.device.activate(mem).unwrap(); diff --git a/src/vmm/src/devices/virtio/vsock/unix/muxer.rs b/src/vmm/src/devices/virtio/vsock/unix/muxer.rs index 4d4b1996237..478d5c7318d 100644 --- a/src/vmm/src/devices/virtio/vsock/unix/muxer.rs +++ b/src/vmm/src/devices/virtio/vsock/unix/muxer.rs @@ -24,11 +24,12 @@ /// destination port to which it wants to connect); /// 3. Some event was triggered for a connected Unix socket, that belongs to a /// `VsockConnection`. -/// The muxer gets notified about all of these events, because, as a `VsockEpollListener` -/// implementor, it gets to register a nested epoll FD into the main VMM epolling loop. All -/// other pollable FDs are then registered under this nested epoll FD. -/// To route all these events to their handlers, the muxer uses another `HashMap` object, -/// mapping `RawFd`s to `EpollListener`s. +/// +/// The muxer gets notified about all of these events, because, as a `VsockEpollListener` +/// implementor, it gets to register a nested epoll FD into the main VMM epolling loop. All +/// other pollable FDs are then registered under this nested epoll FD. +/// To route all these events to their handlers, the muxer uses another `HashMap` object, +/// mapping `RawFd`s to `EpollListener`s. use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::io::Read; @@ -43,7 +44,7 @@ use super::super::defs::uapi; use super::super::{VsockBackend, VsockChannel, VsockEpollListener, VsockError}; use super::muxer_killq::MuxerKillQ; use super::muxer_rxq::MuxerRxQ; -use super::{defs, MuxerConnection, VsockUnixBackendError}; +use super::{MuxerConnection, VsockUnixBackendError, defs}; use crate::devices::virtio::vsock::metrics::METRICS; use crate::devices::virtio::vsock::packet::{VsockPacketRx, VsockPacketTx}; use crate::logger::IncMetric; diff --git a/src/vmm/src/devices/virtio/vsock/unix/muxer_killq.rs b/src/vmm/src/devices/virtio/vsock/unix/muxer_killq.rs index 360dfdf49e9..dc94b38fe36 100644 --- a/src/vmm/src/devices/virtio/vsock/unix/muxer_killq.rs +++ b/src/vmm/src/devices/virtio/vsock/unix/muxer_killq.rs @@ -28,7 +28,7 @@ use std::collections::{HashMap, VecDeque}; use std::time::Instant; use super::muxer::ConnMapKey; -use super::{defs, MuxerConnection}; +use super::{MuxerConnection, defs}; /// A kill queue item, holding the connection key and the scheduled time for termination. #[derive(Debug, Clone, Copy)] diff --git a/src/vmm/src/devices/virtio/vsock/unix/muxer_rxq.rs b/src/vmm/src/devices/virtio/vsock/unix/muxer_rxq.rs index 8327882a3fb..1b888dfa453 100644 --- a/src/vmm/src/devices/virtio/vsock/unix/muxer_rxq.rs +++ b/src/vmm/src/devices/virtio/vsock/unix/muxer_rxq.rs @@ -19,7 +19,7 @@ use std::collections::{HashMap, VecDeque}; use super::super::VsockChannel; use super::muxer::{ConnMapKey, MuxerRx}; -use super::{defs, MuxerConnection}; +use super::{MuxerConnection, defs}; /// The muxer RX queue. #[derive(Debug)] @@ -67,6 +67,7 @@ impl MuxerRxQ { /// A push will fail when: /// - trying to push a connection key onto an out-of-sync, or full queue; or /// - trying to push an RST onto a queue already full of RSTs. + /// /// RSTs take precedence over connections, because connections can always be queried for /// pending RX data later. Aside from this queue, there is no other storage for RSTs, so /// failing to push one means that we have to drop the packet. diff --git a/src/vmm/src/dumbo/mod.rs b/src/vmm/src/dumbo/mod.rs index e1286c74e93..d39b06d0411 100644 --- a/src/vmm/src/dumbo/mod.rs +++ b/src/vmm/src/dumbo/mod.rs @@ -8,12 +8,12 @@ pub mod tcp; use std::ops::Index; -pub use crate::dumbo::pdu::arp::{EthIPv4ArpFrame, ETH_IPV4_FRAME_LEN}; +pub use crate::dumbo::pdu::arp::{ETH_IPV4_FRAME_LEN, EthIPv4ArpFrame}; pub use crate::dumbo::pdu::ethernet::{ - EthernetFrame, ETHERTYPE_ARP, ETHERTYPE_IPV4, PAYLOAD_OFFSET as ETHERNET_PAYLOAD_OFFSET, + ETHERTYPE_ARP, ETHERTYPE_IPV4, EthernetFrame, PAYLOAD_OFFSET as ETHERNET_PAYLOAD_OFFSET, }; pub use crate::dumbo::pdu::ipv4::{IPv4Packet, PROTOCOL_TCP, PROTOCOL_UDP}; -pub use crate::dumbo::pdu::udp::{UdpDatagram, UDP_HEADER_SIZE}; +pub use crate::dumbo::pdu::udp::{UDP_HEADER_SIZE, UdpDatagram}; use crate::utils::net::mac::MacAddr; /// Represents a generalization of a borrowed `[u8]` slice. diff --git a/src/vmm/src/dumbo/pdu/arp.rs b/src/vmm/src/dumbo/pdu/arp.rs index bccb32154a9..7beef7e4d37 100644 --- a/src/vmm/src/dumbo/pdu/arp.rs +++ b/src/vmm/src/dumbo/pdu/arp.rs @@ -14,7 +14,7 @@ use std::result::Result; use super::bytes::{InnerBytes, NetworkBytes, NetworkBytesMut}; use super::ethernet::{self, ETHERTYPE_IPV4}; -use crate::utils::net::mac::{MacAddr, MAC_ADDR_LEN}; +use crate::utils::net::mac::{MAC_ADDR_LEN, MacAddr}; /// ARP Request operation pub const OPER_REQUEST: u16 = 0x0001; @@ -71,7 +71,7 @@ pub struct EthIPv4ArpFrame<'a, T: 'a> { } #[allow(clippy::len_without_is_empty)] -impl<'a, T: NetworkBytes + Debug> EthIPv4ArpFrame<'a, T> { +impl EthIPv4ArpFrame<'_, T> { /// Interprets the given bytes as an ARP frame, without doing any validity checks beforehand. /// /// # Panics @@ -184,7 +184,7 @@ impl<'a, T: NetworkBytes + Debug> EthIPv4ArpFrame<'a, T> { } } -impl<'a, T: NetworkBytesMut + Debug> EthIPv4ArpFrame<'a, T> { +impl EthIPv4ArpFrame<'_, T> { #[allow(clippy::too_many_arguments)] fn write_raw( buf: T, diff --git a/src/vmm/src/dumbo/pdu/bytes.rs b/src/vmm/src/dumbo/pdu/bytes.rs index 9b87673c211..7d5e2346428 100644 --- a/src/vmm/src/dumbo/pdu/bytes.rs +++ b/src/vmm/src/dumbo/pdu/bytes.rs @@ -134,20 +134,20 @@ pub trait NetworkBytesMut: NetworkBytes + DerefMut { } } -impl<'a> NetworkBytes for &'a [u8] { +impl NetworkBytes for &[u8] { #[inline] fn shrink_unchecked(&mut self, len: usize) { *self = &self[..len]; } } -impl<'a> NetworkBytes for &'a mut [u8] { +impl NetworkBytes for &mut [u8] { #[inline] fn shrink_unchecked(&mut self, len: usize) { *self = &mut std::mem::take(self)[..len]; } } -impl<'a> NetworkBytesMut for &'a mut [u8] {} +impl NetworkBytesMut for &mut [u8] {} // This struct is used as a convenience for any type which contains a generic member implementing // NetworkBytes with a lifetime, so we don't have to also add the PhantomData member each time. We @@ -158,7 +158,7 @@ pub(super) struct InnerBytes<'a, T: 'a> { phantom: PhantomData<&'a T>, } -impl<'a, T: Debug> InnerBytes<'a, T> { +impl InnerBytes<'_, T> { /// Creates a new instance as a wrapper around `bytes`. #[inline] pub fn new(bytes: T) -> Self { @@ -169,7 +169,7 @@ impl<'a, T: Debug> InnerBytes<'a, T> { } } -impl<'a, T: Deref + Debug> Deref for InnerBytes<'a, T> { +impl + Debug> Deref for InnerBytes<'_, T> { type Target = [u8]; #[inline] @@ -178,21 +178,21 @@ impl<'a, T: Deref + Debug> Deref for InnerBytes<'a, T> { } } -impl<'a, T: DerefMut + Debug> DerefMut for InnerBytes<'a, T> { +impl + Debug> DerefMut for InnerBytes<'_, T> { #[inline] fn deref_mut(&mut self) -> &mut [u8] { self.bytes.deref_mut() } } -impl<'a, T: NetworkBytes + Debug> NetworkBytes for InnerBytes<'a, T> { +impl NetworkBytes for InnerBytes<'_, T> { #[inline] fn shrink_unchecked(&mut self, len: usize) { self.bytes.shrink_unchecked(len); } } -impl<'a, T: NetworkBytesMut + Debug> NetworkBytesMut for InnerBytes<'a, T> {} +impl NetworkBytesMut for InnerBytes<'_, T> {} #[cfg(test)] mod tests { diff --git a/src/vmm/src/dumbo/pdu/ethernet.rs b/src/vmm/src/dumbo/pdu/ethernet.rs index 8b3321ca4b8..6b7112e36ea 100644 --- a/src/vmm/src/dumbo/pdu/ethernet.rs +++ b/src/vmm/src/dumbo/pdu/ethernet.rs @@ -7,8 +7,8 @@ use std::fmt::Debug; use std::result::Result; -use super::bytes::{InnerBytes, NetworkBytes, NetworkBytesMut}; use super::Incomplete; +use super::bytes::{InnerBytes, NetworkBytes, NetworkBytesMut}; use crate::dumbo::MacAddr; const DST_MAC_OFFSET: usize = 0; @@ -40,7 +40,7 @@ pub struct EthernetFrame<'a, T: 'a> { } #[allow(clippy::len_without_is_empty)] -impl<'a, T: NetworkBytes + Debug> EthernetFrame<'a, T> { +impl EthernetFrame<'_, T> { /// Interprets `bytes` as an Ethernet frame without any validity checks. /// /// # Panics @@ -101,7 +101,7 @@ impl<'a, T: NetworkBytes + Debug> EthernetFrame<'a, T> { } } -impl<'a, T: NetworkBytesMut + Debug> EthernetFrame<'a, T> { +impl EthernetFrame<'_, T> { /// Attempts to write an Ethernet frame using the given header fields to `buf`. fn new_with_header( buf: T, @@ -255,16 +255,6 @@ mod kani_proofs { } } - mod stubs { - // The current implementation of read_be_u16 function leads to a significant - // performance degradation given a necessary loop unrolling. Using this stub, - // we read the same information from the buffer while avoiding the loop, thus, - // notably improving performance. - pub fn read_be_u16(input: &[u8]) -> u16 { - u16::from_be_bytes([input[0], input[1]]) - } - } - // We consider the MMDS Network Stack spec for all postconditions in the harnesses. // See https://github.com/firecracker-microvm/firecracker/blob/main/docs/mmds/mmds-design.md#mmds-network-stack @@ -519,7 +509,6 @@ mod kani_proofs { #[kani::proof] #[kani::solver(cadical)] - #[kani::stub(crate::utils::byte_order::read_be_u16, stubs::read_be_u16)] fn verify_with_payload_len_unchecked() { // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::(); diff --git a/src/vmm/src/dumbo/pdu/ipv4.rs b/src/vmm/src/dumbo/pdu/ipv4.rs index 20d7694524e..fab5b88eb49 100644 --- a/src/vmm/src/dumbo/pdu/ipv4.rs +++ b/src/vmm/src/dumbo/pdu/ipv4.rs @@ -13,7 +13,7 @@ use std::net::Ipv4Addr; use std::result::Result; use crate::dumbo::pdu::bytes::{InnerBytes, NetworkBytes, NetworkBytesMut}; -use crate::dumbo::pdu::{ethernet, Incomplete}; +use crate::dumbo::pdu::{Incomplete, ethernet}; const VERSION_AND_IHL_OFFSET: usize = 0; const DSCP_AND_ECN_OFFSET: usize = 1; @@ -62,7 +62,7 @@ pub struct IPv4Packet<'a, T: 'a> { } #[allow(clippy::len_without_is_empty)] -impl<'a, T: NetworkBytes + Debug> IPv4Packet<'a, T> { +impl IPv4Packet<'_, T> { /// Interpret `bytes` as an IPv4Packet without checking the validity of the header fields, and /// the length of the inner byte sequence. /// @@ -250,7 +250,7 @@ impl<'a, T: NetworkBytes + Debug> IPv4Packet<'a, T> { } } -impl<'a, T: NetworkBytesMut + Debug> IPv4Packet<'a, T> { +impl IPv4Packet<'_, T> { /// Attempts to write an IPv4 packet header to `buf`, making sure there is enough space. /// /// This method returns an incomplete packet, because the size of the payload might be unknown diff --git a/src/vmm/src/dumbo/pdu/mod.rs b/src/vmm/src/dumbo/pdu/mod.rs index d700474ed02..fc6a24760a7 100644 --- a/src/vmm/src/dumbo/pdu/mod.rs +++ b/src/vmm/src/dumbo/pdu/mod.rs @@ -66,8 +66,7 @@ enum ChecksumProto { /// * `bytes` - Raw bytes of a TCP packet or a UDP datagram /// * `src_addr` - IPv4 source address /// * `dst_addr` - IPv4 destination address -/// * `protocol` - **must** be either `PROTOCOL_TCP` or `PROTOCOL_UDP` defined in -/// `ipv4` module +/// * `protocol` - **must** be either `PROTOCOL_TCP` or `PROTOCOL_UDP` defined in `ipv4` module /// /// More details about TCP checksum computation can be found [here]. /// diff --git a/src/vmm/src/dumbo/pdu/tcp.rs b/src/vmm/src/dumbo/pdu/tcp.rs index 2a884689f83..2ac01227142 100644 --- a/src/vmm/src/dumbo/pdu/tcp.rs +++ b/src/vmm/src/dumbo/pdu/tcp.rs @@ -15,10 +15,10 @@ use std::result::Result; use bitflags::bitflags; -use super::bytes::{InnerBytes, NetworkBytes, NetworkBytesMut}; use super::Incomplete; -use crate::dumbo::pdu::ChecksumProto; +use super::bytes::{InnerBytes, NetworkBytes, NetworkBytesMut}; use crate::dumbo::ByteBuffer; +use crate::dumbo::pdu::ChecksumProto; const SOURCE_PORT_OFFSET: usize = 0; const DESTINATION_PORT_OFFSET: usize = 2; @@ -99,7 +99,7 @@ pub struct TcpSegment<'a, T: 'a> { } #[allow(clippy::len_without_is_empty)] -impl<'a, T: NetworkBytes + Debug> TcpSegment<'a, T> { +impl TcpSegment<'_, T> { /// Returns the source port. #[inline] pub fn source_port(&self) -> u16 { @@ -314,7 +314,7 @@ impl<'a, T: NetworkBytes + Debug> TcpSegment<'a, T> { } } -impl<'a, T: NetworkBytesMut + Debug> TcpSegment<'a, T> { +impl TcpSegment<'_, T> { /// Sets the source port. #[inline] pub fn set_source_port(&mut self, value: u16) -> &mut Self { diff --git a/src/vmm/src/dumbo/pdu/udp.rs b/src/vmm/src/dumbo/pdu/udp.rs index 43ad9c29cef..8acfc4509f4 100644 --- a/src/vmm/src/dumbo/pdu/udp.rs +++ b/src/vmm/src/dumbo/pdu/udp.rs @@ -46,7 +46,7 @@ pub struct UdpDatagram<'a, T: 'a> { } #[allow(clippy::len_without_is_empty)] -impl<'a, T: NetworkBytes + Debug> UdpDatagram<'a, T> { +impl UdpDatagram<'_, T> { /// Interprets `bytes` as a UDP datagram without any validity checks. /// /// # Panics @@ -122,7 +122,7 @@ impl<'a, T: NetworkBytes + Debug> UdpDatagram<'a, T> { } } -impl<'a, T: NetworkBytesMut + Debug> UdpDatagram<'a, T> { +impl UdpDatagram<'_, T> { /// Writes an incomplete UDP datagram, which is missing the `checksum`, `src_port` and /// `dst_port` fields. /// diff --git a/src/vmm/src/dumbo/tcp/connection.rs b/src/vmm/src/dumbo/tcp/connection.rs index 8036f428318..016a44477a7 100644 --- a/src/vmm/src/dumbo/tcp/connection.rs +++ b/src/vmm/src/dumbo/tcp/connection.rs @@ -12,13 +12,13 @@ use std::num::{NonZeroU16, NonZeroU64, NonZeroUsize, Wrapping}; use bitflags::bitflags; use vmm_sys_util::rand::xor_pseudo_rng_u32; +use crate::dumbo::ByteBuffer; +use crate::dumbo::pdu::Incomplete; use crate::dumbo::pdu::bytes::NetworkBytes; use crate::dumbo::pdu::tcp::{Flags as TcpFlags, TcpError as TcpSegmentError, TcpSegment}; -use crate::dumbo::pdu::Incomplete; use crate::dumbo::tcp::{ - seq_after, seq_at_or_after, NextSegmentStatus, RstConfig, MAX_WINDOW_SIZE, MSS_DEFAULT, + MAX_WINDOW_SIZE, MSS_DEFAULT, NextSegmentStatus, RstConfig, seq_after, seq_at_or_after, }; -use crate::dumbo::ByteBuffer; bitflags! { // We use a set of flags, instead of a state machine, to represent the connection status. Some @@ -815,7 +815,7 @@ impl Connection { /// * `mss_reserved` - How much (if anything) of the MSS value has been already used at the /// lower layers (by IP options, for example). This will be zero most of the time. /// * `payload_src` - References a buffer which contains data to send, and also specifies the - /// sequence number associated with the first byte from that that buffer. + /// sequence number associated with the first byte from that buffer. /// * `now` - An opaque timestamp representing the current moment in time. /// /// [`MAX_WINDOW_SIZE`]: ../constant.MAX_WINDOW_SIZE.html diff --git a/src/vmm/src/dumbo/tcp/endpoint.rs b/src/vmm/src/dumbo/tcp/endpoint.rs index 92058d83ee9..2fbb8466564 100644 --- a/src/vmm/src/dumbo/tcp/endpoint.rs +++ b/src/vmm/src/dumbo/tcp/endpoint.rs @@ -17,11 +17,11 @@ use std::num::{NonZeroU16, NonZeroU64, Wrapping}; use micro_http::{Body, Request, RequestError, Response, StatusCode, Version}; use utils::time::timestamp_cycles; +use crate::dumbo::pdu::Incomplete; use crate::dumbo::pdu::bytes::NetworkBytes; use crate::dumbo::pdu::tcp::TcpSegment; -use crate::dumbo::pdu::Incomplete; use crate::dumbo::tcp::connection::{Connection, PassiveOpenError, RecvStatusFlags}; -use crate::dumbo::tcp::{seq_after, NextSegmentStatus, MAX_WINDOW_SIZE}; +use crate::dumbo::tcp::{MAX_WINDOW_SIZE, NextSegmentStatus, seq_after}; use crate::logger::{IncMetric, METRICS}; // TODO: These are currently expressed in cycles. Normally, they would be the equivalent of a @@ -281,9 +281,8 @@ impl Endpoint { tcp_payload_src, timestamp_cycles(), ) { - Ok(write_result) => write_result.map(|segment| { + Ok(write_result) => write_result.inspect(|segment| { self.response_seq += Wrapping(u32::from(segment.inner().payload_len())); - segment }), Err(_) => { METRICS.mmds.tx_errors.inc(); @@ -630,9 +629,11 @@ mod tests { Expect: 100-continue\r\n\ Transfer-Encoding: identity; q=0\r\n\ Content-Length: 26\r\n\r\nthis is not\n\r\na json \nbody"; - assert!(parse_request_bytes(request_bytes, mock_callback) - .body() - .is_none()); + assert!( + parse_request_bytes(request_bytes, mock_callback) + .body() + .is_none() + ); let request_bytes = b"PATCH http://localhost/home HTTP/1.1\r\n\ Expect: 100-continue\r\n\ diff --git a/src/vmm/src/gdb/arch/aarch64.rs b/src/vmm/src/gdb/arch/aarch64.rs index eec7cfa9df8..a462d677ff3 100644 --- a/src/vmm/src/gdb/arch/aarch64.rs +++ b/src/vmm/src/gdb/arch/aarch64.rs @@ -5,18 +5,18 @@ use std::mem::offset_of; use gdbstub_arch::aarch64::reg::AArch64CoreRegs as CoreRegs; use kvm_bindings::{ - kvm_guest_debug, kvm_regs, user_pt_regs, KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP, - KVM_GUESTDBG_USE_HW, KVM_GUESTDBG_USE_SW_BP, KVM_REG_ARM64, KVM_REG_ARM_CORE, KVM_REG_SIZE_U64, + KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP, KVM_GUESTDBG_USE_HW, KVM_GUESTDBG_USE_SW_BP, + KVM_REG_ARM_CORE, KVM_REG_ARM64, KVM_REG_SIZE_U64, kvm_guest_debug, kvm_regs, user_pt_regs, }; use kvm_ioctls::VcpuFd; use vm_memory::{Bytes, GuestAddress}; +use crate::Vmm; use crate::arch::aarch64::regs::{ - arm64_core_reg_id, Aarch64RegisterVec, ID_AA64MMFR0_EL1, TCR_EL1, TTBR1_EL1, + Aarch64RegisterVec, ID_AA64MMFR0_EL1, TCR_EL1, TTBR1_EL1, arm64_core_reg_id, }; use crate::arch::aarch64::vcpu::get_registers; use crate::gdb::target::GdbTargetError; -use crate::Vmm; /// Configures the number of bytes required for a software breakpoint. /// @@ -63,7 +63,9 @@ const PTE_ADDRESS_MASK: u64 = !0b111u64; /// Read a u64 value from a guest memory address fn read_address(vmm: &Vmm, address: u64) -> Result { let mut buf = [0; 8]; - vmm.guest_memory().read(&mut buf, GuestAddress(address))?; + vmm.vm + .guest_memory() + .read(&mut buf, GuestAddress(address))?; Ok(u64::from_le_bytes(buf)) } diff --git a/src/vmm/src/gdb/arch/x86.rs b/src/vmm/src/gdb/arch/x86.rs index 16d6789b100..f8df58bedef 100644 --- a/src/vmm/src/gdb/arch/x86.rs +++ b/src/vmm/src/gdb/arch/x86.rs @@ -6,9 +6,9 @@ use kvm_bindings::*; use kvm_ioctls::VcpuFd; use vm_memory::GuestAddress; +use crate::Vmm; use crate::gdb::target::GdbTargetError; use crate::logger::error; -use crate::Vmm; /// Sets the 9th (Global Exact Breakpoint enable) and the 10th (always 1) bits for the DR7 debug /// control register diff --git a/src/vmm/src/gdb/event_loop.rs b/src/vmm/src/gdb/event_loop.rs index ae4de0e64d7..35fe2039f77 100644 --- a/src/vmm/src/gdb/event_loop.rs +++ b/src/vmm/src/gdb/event_loop.rs @@ -14,9 +14,9 @@ use gdbstub::target::Target; use kvm_ioctls::VcpuFd; use vm_memory::GuestAddress; -use super::target::{vcpuid_to_tid, FirecrackerTarget, GdbTargetError}; -use crate::logger::{error, trace}; +use super::target::{FirecrackerTarget, GdbTargetError, vcpuid_to_tid}; use crate::Vmm; +use crate::logger::{error, trace}; /// Starts the GDB event loop which acts as a proxy between the Vcpus and GDB pub fn event_loop( diff --git a/src/vmm/src/gdb/mod.rs b/src/vmm/src/gdb/mod.rs index 9d209541425..23d9965b1aa 100644 --- a/src/vmm/src/gdb/mod.rs +++ b/src/vmm/src/gdb/mod.rs @@ -19,8 +19,8 @@ use kvm_ioctls::VcpuFd; use target::GdbTargetError; use vm_memory::GuestAddress; -use crate::logger::trace; use crate::Vmm; +use crate::logger::trace; /// Kickstarts the GDB debugging process, it takes in the VMM object, a slice of /// the paused Vcpu's, the GDB event queue which is used as a mechanism for the Vcpu's to notify diff --git a/src/vmm/src/gdb/target.rs b/src/vmm/src/gdb/target.rs index fc2da289e8a..666861a0adb 100644 --- a/src/vmm/src/gdb/target.rs +++ b/src/vmm/src/gdb/target.rs @@ -9,31 +9,31 @@ use arrayvec::ArrayVec; use gdbstub::arch::Arch; use gdbstub::common::{Signal, Tid}; use gdbstub::stub::{BaseStopReason, MultiThreadStopReason}; +use gdbstub::target::ext::base::BaseOps; use gdbstub::target::ext::base::multithread::{ MultiThreadBase, MultiThreadResume, MultiThreadResumeOps, MultiThreadSingleStep, MultiThreadSingleStepOps, }; -use gdbstub::target::ext::base::BaseOps; use gdbstub::target::ext::breakpoints::{ Breakpoints, BreakpointsOps, HwBreakpoint, HwBreakpointOps, SwBreakpoint, SwBreakpointOps, }; use gdbstub::target::ext::thread_extra_info::{ThreadExtraInfo, ThreadExtraInfoOps}; use gdbstub::target::{Target, TargetError, TargetResult}; #[cfg(target_arch = "aarch64")] -use gdbstub_arch::aarch64::reg::AArch64CoreRegs as CoreRegs; -#[cfg(target_arch = "aarch64")] use gdbstub_arch::aarch64::AArch64 as GdbArch; -#[cfg(target_arch = "x86_64")] -use gdbstub_arch::x86::reg::X86_64CoreRegs as CoreRegs; +#[cfg(target_arch = "aarch64")] +use gdbstub_arch::aarch64::reg::AArch64CoreRegs as CoreRegs; #[cfg(target_arch = "x86_64")] use gdbstub_arch::x86::X86_64_SSE as GdbArch; +#[cfg(target_arch = "x86_64")] +use gdbstub_arch::x86::reg::X86_64CoreRegs as CoreRegs; use kvm_ioctls::VcpuFd; use vm_memory::{Bytes, GuestAddress, GuestMemoryError}; use super::arch; +use crate::arch::GUEST_PAGE_SIZE; #[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::vcpu::VcpuError as AarchVcpuError; -use crate::arch::PAGE_SIZE; +use crate::arch::aarch64::vcpu::VcpuArchError as AarchVcpuError; use crate::logger::{error, info}; use crate::utils::u64_to_usize; use crate::vstate::vcpu::VcpuSendEventError; @@ -390,16 +390,17 @@ impl MultiThreadBase for FirecrackerTarget { while !data.is_empty() { let gpa = arch::translate_gva(&vcpu_state.vcpu_fd, gva, &vmm).map_err(|e| { - error!("Error {e:?} translating gva on read address: {gva:X}"); + error!("Error {e:?} translating gva on read address: {gva:#X}"); })?; // Compute the amount space left in the page after the gpa let read_len = std::cmp::min( data.len(), - PAGE_SIZE - (u64_to_usize(gpa) & (PAGE_SIZE - 1)), + GUEST_PAGE_SIZE - (u64_to_usize(gpa) & (GUEST_PAGE_SIZE - 1)), ); - vmm.guest_memory() + vmm.vm + .guest_memory() .read(&mut data[..read_len], GuestAddress(gpa as u64)) .map_err(|e| { error!("Error reading memory {e:?} gpa is {gpa}"); @@ -424,19 +425,20 @@ impl MultiThreadBase for FirecrackerTarget { while !data.is_empty() { let gpa = arch::translate_gva(&vcpu_state.vcpu_fd, gva, &vmm).map_err(|e| { - error!("Error {e:?} translating gva on read address: {gva:X}"); + error!("Error {e:?} translating gva on read address: {gva:#X}"); })?; // Compute the amount space left in the page after the gpa let write_len = std::cmp::min( data.len(), - PAGE_SIZE - (u64_to_usize(gpa) & (PAGE_SIZE - 1)), + GUEST_PAGE_SIZE - (u64_to_usize(gpa) & (GUEST_PAGE_SIZE - 1)), ); - vmm.guest_memory() + vmm.vm + .guest_memory() .write(&data[..write_len], GuestAddress(gpa)) .map_err(|e| { - error!("Error {e:?} writing memory at {gpa:X}"); + error!("Error {e:?} writing memory at {gpa:#X}"); })?; data = &data[write_len..]; diff --git a/src/vmm/src/initrd.rs b/src/vmm/src/initrd.rs new file mode 100644 index 00000000000..9dfcd8bc16e --- /dev/null +++ b/src/vmm/src/initrd.rs @@ -0,0 +1,140 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use std::fs::File; +use std::os::unix::fs::MetadataExt; + +use vm_memory::{GuestAddress, GuestMemory, ReadVolatile, VolatileMemoryError}; + +use crate::arch::initrd_load_addr; +use crate::utils::u64_to_usize; +use crate::vmm_config::boot_source::BootConfig; +use crate::vstate::memory::GuestMemoryMmap; + +/// Errors associated with initrd loading. +#[derive(Debug, thiserror::Error, displaydoc::Display)] +pub enum InitrdError { + /// Failed to compute the initrd address. + Address, + /// Cannot load initrd due to an invalid memory configuration. + Load, + /// Cannot image metadata: {0} + Metadata(std::io::Error), + /// Cannot copy initrd file fd: {0} + CloneFd(std::io::Error), + /// Cannot load initrd due to an invalid image: {0} + Read(VolatileMemoryError), +} + +/// Type for passing information about the initrd in the guest memory. +#[derive(Debug)] +pub struct InitrdConfig { + /// Load address of initrd in guest memory + pub address: GuestAddress, + /// Size of initrd in guest memory + pub size: usize, +} + +impl InitrdConfig { + /// Load initrd into guest memory based on the boot config. + pub fn from_config( + boot_cfg: &BootConfig, + vm_memory: &GuestMemoryMmap, + ) -> Result, InitrdError> { + Ok(match &boot_cfg.initrd_file { + Some(f) => { + let f = f.try_clone().map_err(InitrdError::CloneFd)?; + Some(Self::from_file(vm_memory, f)?) + } + None => None, + }) + } + + /// Loads the initrd from a file into guest memory. + pub fn from_file(vm_memory: &GuestMemoryMmap, mut file: File) -> Result { + let size = file.metadata().map_err(InitrdError::Metadata)?.size(); + let size = u64_to_usize(size); + let Some(address) = initrd_load_addr(vm_memory, size) else { + return Err(InitrdError::Address); + }; + let mut slice = vm_memory + .get_slice(GuestAddress(address), size) + .map_err(|_| InitrdError::Load)?; + file.read_exact_volatile(&mut slice) + .map_err(InitrdError::Read)?; + + Ok(InitrdConfig { + address: GuestAddress(address), + size, + }) + } +} + +#[cfg(test)] +mod tests { + use std::io::{Seek, SeekFrom, Write}; + + use vmm_sys_util::tempfile::TempFile; + + use super::*; + use crate::arch::GUEST_PAGE_SIZE; + use crate::test_utils::{single_region_mem, single_region_mem_at}; + + fn make_test_bin() -> Vec { + let mut fake_bin = Vec::new(); + fake_bin.resize(1_000_000, 0xAA); + fake_bin + } + + #[test] + // Test that loading the initrd is successful on different archs. + fn test_load_initrd() { + let image = make_test_bin(); + + let mem_size: usize = image.len() * 2 + GUEST_PAGE_SIZE; + + let tempfile = TempFile::new().unwrap(); + let mut tempfile = tempfile.into_file(); + tempfile.write_all(&image).unwrap(); + + #[cfg(target_arch = "x86_64")] + let gm = single_region_mem(mem_size); + + #[cfg(target_arch = "aarch64")] + let gm = single_region_mem(mem_size + crate::arch::aarch64::layout::FDT_MAX_SIZE); + + // Need to reset the cursor to read initrd properly. + tempfile.seek(SeekFrom::Start(0)).unwrap(); + let initrd = InitrdConfig::from_file(&gm, tempfile).unwrap(); + assert!(gm.address_in_range(initrd.address)); + assert_eq!(initrd.size, image.len()); + } + + #[test] + fn test_load_initrd_no_memory() { + let gm = single_region_mem(79); + let image = make_test_bin(); + let tempfile = TempFile::new().unwrap(); + let mut tempfile = tempfile.into_file(); + tempfile.write_all(&image).unwrap(); + + // Need to reset the cursor to read initrd properly. + tempfile.seek(SeekFrom::Start(0)).unwrap(); + let res = InitrdConfig::from_file(&gm, tempfile); + assert!(matches!(res, Err(InitrdError::Address)), "{:?}", res); + } + + #[test] + fn test_load_initrd_unaligned() { + let image = vec![1, 2, 3, 4]; + let tempfile = TempFile::new().unwrap(); + let mut tempfile = tempfile.into_file(); + tempfile.write_all(&image).unwrap(); + let gm = single_region_mem_at(GUEST_PAGE_SIZE as u64 + 1, image.len() * 2); + + // Need to reset the cursor to read initrd properly. + tempfile.seek(SeekFrom::Start(0)).unwrap(); + let res = InitrdConfig::from_file(&gm, tempfile); + assert!(matches!(res, Err(InitrdError::Address)), "{:?}", res); + } +} diff --git a/src/vmm/src/io_uring/gen.rs b/src/vmm/src/io_uring/gen.rs deleted file mode 100644 index c18a3aa36ec..00000000000 --- a/src/vmm/src/io_uring/gen.rs +++ /dev/null @@ -1,1712 +0,0 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// automatically generated by tools/bindgen.sh - -#![allow( - non_camel_case_types, - non_upper_case_globals, - dead_code, - non_snake_case, - clippy::ptr_as_ptr, - clippy::undocumented_unsafe_blocks, - missing_debug_implementations, - clippy::tests_outside_test_module -)] - -#[repr(C)] -#[derive(Default)] -pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); -impl __IncompleteArrayField { - #[inline] - pub const fn new() -> Self { - __IncompleteArrayField(::std::marker::PhantomData, []) - } - #[inline] - pub fn as_ptr(&self) -> *const T { - self as *const _ as *const T - } - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut T { - self as *mut _ as *mut T - } - #[inline] - pub unsafe fn as_slice(&self, len: usize) -> &[T] { - ::std::slice::from_raw_parts(self.as_ptr(), len) - } - #[inline] - pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { - ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) - } -} -impl ::std::fmt::Debug for __IncompleteArrayField { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - fmt.write_str("__IncompleteArrayField") - } -} -pub const IORING_SETUP_IOPOLL: u32 = 1; -pub const IORING_SETUP_SQPOLL: u32 = 2; -pub const IORING_SETUP_SQ_AFF: u32 = 4; -pub const IORING_SETUP_CQSIZE: u32 = 8; -pub const IORING_SETUP_CLAMP: u32 = 16; -pub const IORING_SETUP_ATTACH_WQ: u32 = 32; -pub const IORING_SETUP_R_DISABLED: u32 = 64; -pub const IORING_FSYNC_DATASYNC: u32 = 1; -pub const IORING_TIMEOUT_ABS: u32 = 1; -pub const IORING_TIMEOUT_UPDATE: u32 = 2; -pub const IORING_TIMEOUT_BOOTTIME: u32 = 4; -pub const IORING_TIMEOUT_REALTIME: u32 = 8; -pub const IORING_LINK_TIMEOUT_UPDATE: u32 = 16; -pub const IORING_TIMEOUT_CLOCK_MASK: u32 = 12; -pub const IORING_TIMEOUT_UPDATE_MASK: u32 = 18; -pub const IORING_POLL_ADD_MULTI: u32 = 1; -pub const IORING_POLL_UPDATE_EVENTS: u32 = 2; -pub const IORING_POLL_UPDATE_USER_DATA: u32 = 4; -pub const IORING_CQE_F_BUFFER: u32 = 1; -pub const IORING_CQE_F_MORE: u32 = 2; -pub const IORING_OFF_SQ_RING: u32 = 0; -pub const IORING_OFF_CQ_RING: u32 = 134217728; -pub const IORING_OFF_SQES: u32 = 268435456; -pub const IORING_SQ_NEED_WAKEUP: u32 = 1; -pub const IORING_SQ_CQ_OVERFLOW: u32 = 2; -pub const IORING_CQ_EVENTFD_DISABLED: u32 = 1; -pub const IORING_ENTER_GETEVENTS: u32 = 1; -pub const IORING_ENTER_SQ_WAKEUP: u32 = 2; -pub const IORING_ENTER_SQ_WAIT: u32 = 4; -pub const IORING_ENTER_EXT_ARG: u32 = 8; -pub const IORING_FEAT_SINGLE_MMAP: u32 = 1; -pub const IORING_FEAT_NODROP: u32 = 2; -pub const IORING_FEAT_SUBMIT_STABLE: u32 = 4; -pub const IORING_FEAT_RW_CUR_POS: u32 = 8; -pub const IORING_FEAT_CUR_PERSONALITY: u32 = 16; -pub const IORING_FEAT_FAST_POLL: u32 = 32; -pub const IORING_FEAT_POLL_32BITS: u32 = 64; -pub const IORING_FEAT_SQPOLL_NONFIXED: u32 = 128; -pub const IORING_FEAT_EXT_ARG: u32 = 256; -pub const IORING_FEAT_NATIVE_WORKERS: u32 = 512; -pub const IORING_FEAT_RSRC_TAGS: u32 = 1024; -pub const IORING_REGISTER_FILES_SKIP: i32 = -2; -pub const IO_URING_OP_SUPPORTED: u32 = 1; -pub type __u8 = ::std::os::raw::c_uchar; -pub type __u16 = ::std::os::raw::c_ushort; -pub type __s32 = ::std::os::raw::c_int; -pub type __u32 = ::std::os::raw::c_uint; -pub type __u64 = ::std::os::raw::c_ulonglong; -pub type __kernel_rwf_t = ::std::os::raw::c_int; -#[repr(C)] -#[derive(Copy, Clone)] -pub struct io_uring_sqe { - pub opcode: __u8, - pub flags: __u8, - pub ioprio: __u16, - pub fd: __s32, - pub __bindgen_anon_1: io_uring_sqe__bindgen_ty_1, - pub __bindgen_anon_2: io_uring_sqe__bindgen_ty_2, - pub len: __u32, - pub __bindgen_anon_3: io_uring_sqe__bindgen_ty_3, - pub user_data: __u64, - pub __bindgen_anon_4: io_uring_sqe__bindgen_ty_4, - pub personality: __u16, - pub __bindgen_anon_5: io_uring_sqe__bindgen_ty_5, - pub __pad2: [__u64; 2usize], -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union io_uring_sqe__bindgen_ty_1 { - pub off: __u64, - pub addr2: __u64, -} -#[test] -fn bindgen_test_layout_io_uring_sqe__bindgen_ty_1() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(io_uring_sqe__bindgen_ty_1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_sqe__bindgen_ty_1)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).off) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_1), - "::", - stringify!(off) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).addr2) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_1), - "::", - stringify!(addr2) - ) - ); -} -impl Default for io_uring_sqe__bindgen_ty_1 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union io_uring_sqe__bindgen_ty_2 { - pub addr: __u64, - pub splice_off_in: __u64, -} -#[test] -fn bindgen_test_layout_io_uring_sqe__bindgen_ty_2() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(io_uring_sqe__bindgen_ty_2)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_sqe__bindgen_ty_2)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).addr) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_2), - "::", - stringify!(addr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).splice_off_in) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_2), - "::", - stringify!(splice_off_in) - ) - ); -} -impl Default for io_uring_sqe__bindgen_ty_2 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union io_uring_sqe__bindgen_ty_3 { - pub rw_flags: __kernel_rwf_t, - pub fsync_flags: __u32, - pub poll_events: __u16, - pub poll32_events: __u32, - pub sync_range_flags: __u32, - pub msg_flags: __u32, - pub timeout_flags: __u32, - pub accept_flags: __u32, - pub cancel_flags: __u32, - pub open_flags: __u32, - pub statx_flags: __u32, - pub fadvise_advice: __u32, - pub splice_flags: __u32, - pub rename_flags: __u32, - pub unlink_flags: __u32, - pub hardlink_flags: __u32, -} -#[test] -fn bindgen_test_layout_io_uring_sqe__bindgen_ty_3() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 4usize, - concat!("Size of: ", stringify!(io_uring_sqe__bindgen_ty_3)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(io_uring_sqe__bindgen_ty_3)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).rw_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(rw_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).fsync_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(fsync_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).poll_events) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(poll_events) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).poll32_events) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(poll32_events) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sync_range_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(sync_range_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).msg_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(msg_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).timeout_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(timeout_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).accept_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(accept_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).cancel_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(cancel_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).open_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(open_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).statx_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(statx_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).fadvise_advice) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(fadvise_advice) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).splice_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(splice_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).rename_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(rename_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).unlink_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(unlink_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).hardlink_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_3), - "::", - stringify!(hardlink_flags) - ) - ); -} -impl Default for io_uring_sqe__bindgen_ty_3 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[repr(C, packed)] -#[derive(Copy, Clone)] -pub union io_uring_sqe__bindgen_ty_4 { - pub buf_index: __u16, - pub buf_group: __u16, -} -#[test] -fn bindgen_test_layout_io_uring_sqe__bindgen_ty_4() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 2usize, - concat!("Size of: ", stringify!(io_uring_sqe__bindgen_ty_4)) - ); - assert_eq!( - ::std::mem::align_of::(), - 1usize, - concat!("Alignment of ", stringify!(io_uring_sqe__bindgen_ty_4)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).buf_index) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_4), - "::", - stringify!(buf_index) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).buf_group) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_4), - "::", - stringify!(buf_group) - ) - ); -} -impl Default for io_uring_sqe__bindgen_ty_4 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union io_uring_sqe__bindgen_ty_5 { - pub splice_fd_in: __s32, - pub file_index: __u32, -} -#[test] -fn bindgen_test_layout_io_uring_sqe__bindgen_ty_5() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 4usize, - concat!("Size of: ", stringify!(io_uring_sqe__bindgen_ty_5)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(io_uring_sqe__bindgen_ty_5)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).splice_fd_in) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_5), - "::", - stringify!(splice_fd_in) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).file_index) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe__bindgen_ty_5), - "::", - stringify!(file_index) - ) - ); -} -impl Default for io_uring_sqe__bindgen_ty_5 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[test] -fn bindgen_test_layout_io_uring_sqe() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 64usize, - concat!("Size of: ", stringify!(io_uring_sqe)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_sqe)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).opcode) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe), - "::", - stringify!(opcode) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe), - "::", - stringify!(flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ioprio) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe), - "::", - stringify!(ioprio) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).fd) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe), - "::", - stringify!(fd) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).len) as usize - ptr as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe), - "::", - stringify!(len) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).user_data) as usize - ptr as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe), - "::", - stringify!(user_data) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).personality) as usize - ptr as usize }, - 42usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe), - "::", - stringify!(personality) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).__pad2) as usize - ptr as usize }, - 48usize, - concat!( - "Offset of field: ", - stringify!(io_uring_sqe), - "::", - stringify!(__pad2) - ) - ); -} -impl Default for io_uring_sqe { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -pub const IOSQE_FIXED_FILE_BIT: _bindgen_ty_1 = 0; -pub const IOSQE_IO_DRAIN_BIT: _bindgen_ty_1 = 1; -pub const IOSQE_IO_LINK_BIT: _bindgen_ty_1 = 2; -pub const IOSQE_IO_HARDLINK_BIT: _bindgen_ty_1 = 3; -pub const IOSQE_ASYNC_BIT: _bindgen_ty_1 = 4; -pub const IOSQE_BUFFER_SELECT_BIT: _bindgen_ty_1 = 5; -pub type _bindgen_ty_1 = ::std::os::raw::c_uint; -pub const IORING_OP_NOP: _bindgen_ty_2 = 0; -pub const IORING_OP_READV: _bindgen_ty_2 = 1; -pub const IORING_OP_WRITEV: _bindgen_ty_2 = 2; -pub const IORING_OP_FSYNC: _bindgen_ty_2 = 3; -pub const IORING_OP_READ_FIXED: _bindgen_ty_2 = 4; -pub const IORING_OP_WRITE_FIXED: _bindgen_ty_2 = 5; -pub const IORING_OP_POLL_ADD: _bindgen_ty_2 = 6; -pub const IORING_OP_POLL_REMOVE: _bindgen_ty_2 = 7; -pub const IORING_OP_SYNC_FILE_RANGE: _bindgen_ty_2 = 8; -pub const IORING_OP_SENDMSG: _bindgen_ty_2 = 9; -pub const IORING_OP_RECVMSG: _bindgen_ty_2 = 10; -pub const IORING_OP_TIMEOUT: _bindgen_ty_2 = 11; -pub const IORING_OP_TIMEOUT_REMOVE: _bindgen_ty_2 = 12; -pub const IORING_OP_ACCEPT: _bindgen_ty_2 = 13; -pub const IORING_OP_ASYNC_CANCEL: _bindgen_ty_2 = 14; -pub const IORING_OP_LINK_TIMEOUT: _bindgen_ty_2 = 15; -pub const IORING_OP_CONNECT: _bindgen_ty_2 = 16; -pub const IORING_OP_FALLOCATE: _bindgen_ty_2 = 17; -pub const IORING_OP_OPENAT: _bindgen_ty_2 = 18; -pub const IORING_OP_CLOSE: _bindgen_ty_2 = 19; -pub const IORING_OP_FILES_UPDATE: _bindgen_ty_2 = 20; -pub const IORING_OP_STATX: _bindgen_ty_2 = 21; -pub const IORING_OP_READ: _bindgen_ty_2 = 22; -pub const IORING_OP_WRITE: _bindgen_ty_2 = 23; -pub const IORING_OP_FADVISE: _bindgen_ty_2 = 24; -pub const IORING_OP_MADVISE: _bindgen_ty_2 = 25; -pub const IORING_OP_SEND: _bindgen_ty_2 = 26; -pub const IORING_OP_RECV: _bindgen_ty_2 = 27; -pub const IORING_OP_OPENAT2: _bindgen_ty_2 = 28; -pub const IORING_OP_EPOLL_CTL: _bindgen_ty_2 = 29; -pub const IORING_OP_SPLICE: _bindgen_ty_2 = 30; -pub const IORING_OP_PROVIDE_BUFFERS: _bindgen_ty_2 = 31; -pub const IORING_OP_REMOVE_BUFFERS: _bindgen_ty_2 = 32; -pub const IORING_OP_TEE: _bindgen_ty_2 = 33; -pub const IORING_OP_SHUTDOWN: _bindgen_ty_2 = 34; -pub const IORING_OP_RENAMEAT: _bindgen_ty_2 = 35; -pub const IORING_OP_UNLINKAT: _bindgen_ty_2 = 36; -pub const IORING_OP_LAST: _bindgen_ty_2 = 37; -pub type _bindgen_ty_2 = ::std::os::raw::c_uint; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_uring_cqe { - pub user_data: __u64, - pub res: __s32, - pub flags: __u32, -} -#[test] -fn bindgen_test_layout_io_uring_cqe() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(io_uring_cqe)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_cqe)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).user_data) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_cqe), - "::", - stringify!(user_data) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).res) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(io_uring_cqe), - "::", - stringify!(res) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(io_uring_cqe), - "::", - stringify!(flags) - ) - ); -} -pub const IORING_CQE_BUFFER_SHIFT: _bindgen_ty_3 = 16; -pub type _bindgen_ty_3 = ::std::os::raw::c_uint; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_sqring_offsets { - pub head: __u32, - pub tail: __u32, - pub ring_mask: __u32, - pub ring_entries: __u32, - pub flags: __u32, - pub dropped: __u32, - pub array: __u32, - pub resv1: __u32, - pub resv2: __u64, -} -#[test] -fn bindgen_test_layout_io_sqring_offsets() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 40usize, - concat!("Size of: ", stringify!(io_sqring_offsets)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_sqring_offsets)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).head) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_sqring_offsets), - "::", - stringify!(head) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).tail) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_sqring_offsets), - "::", - stringify!(tail) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ring_mask) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(io_sqring_offsets), - "::", - stringify!(ring_mask) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ring_entries) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(io_sqring_offsets), - "::", - stringify!(ring_entries) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(io_sqring_offsets), - "::", - stringify!(flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dropped) as usize - ptr as usize }, - 20usize, - concat!( - "Offset of field: ", - stringify!(io_sqring_offsets), - "::", - stringify!(dropped) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).array) as usize - ptr as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(io_sqring_offsets), - "::", - stringify!(array) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv1) as usize - ptr as usize }, - 28usize, - concat!( - "Offset of field: ", - stringify!(io_sqring_offsets), - "::", - stringify!(resv1) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv2) as usize - ptr as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(io_sqring_offsets), - "::", - stringify!(resv2) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_cqring_offsets { - pub head: __u32, - pub tail: __u32, - pub ring_mask: __u32, - pub ring_entries: __u32, - pub overflow: __u32, - pub cqes: __u32, - pub flags: __u32, - pub resv1: __u32, - pub resv2: __u64, -} -#[test] -fn bindgen_test_layout_io_cqring_offsets() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 40usize, - concat!("Size of: ", stringify!(io_cqring_offsets)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_cqring_offsets)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).head) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_cqring_offsets), - "::", - stringify!(head) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).tail) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_cqring_offsets), - "::", - stringify!(tail) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ring_mask) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(io_cqring_offsets), - "::", - stringify!(ring_mask) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ring_entries) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(io_cqring_offsets), - "::", - stringify!(ring_entries) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).overflow) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(io_cqring_offsets), - "::", - stringify!(overflow) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).cqes) as usize - ptr as usize }, - 20usize, - concat!( - "Offset of field: ", - stringify!(io_cqring_offsets), - "::", - stringify!(cqes) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(io_cqring_offsets), - "::", - stringify!(flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv1) as usize - ptr as usize }, - 28usize, - concat!( - "Offset of field: ", - stringify!(io_cqring_offsets), - "::", - stringify!(resv1) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv2) as usize - ptr as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(io_cqring_offsets), - "::", - stringify!(resv2) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_uring_params { - pub sq_entries: __u32, - pub cq_entries: __u32, - pub flags: __u32, - pub sq_thread_cpu: __u32, - pub sq_thread_idle: __u32, - pub features: __u32, - pub wq_fd: __u32, - pub resv: [__u32; 3usize], - pub sq_off: io_sqring_offsets, - pub cq_off: io_cqring_offsets, -} -#[test] -fn bindgen_test_layout_io_uring_params() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 120usize, - concat!("Size of: ", stringify!(io_uring_params)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_params)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sq_entries) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(sq_entries) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).cq_entries) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(cq_entries) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sq_thread_cpu) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(sq_thread_cpu) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sq_thread_idle) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(sq_thread_idle) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).features) as usize - ptr as usize }, - 20usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(features) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).wq_fd) as usize - ptr as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(wq_fd) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize }, - 28usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(resv) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sq_off) as usize - ptr as usize }, - 40usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(sq_off) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).cq_off) as usize - ptr as usize }, - 80usize, - concat!( - "Offset of field: ", - stringify!(io_uring_params), - "::", - stringify!(cq_off) - ) - ); -} -pub const IORING_REGISTER_BUFFERS: _bindgen_ty_4 = 0; -pub const IORING_UNREGISTER_BUFFERS: _bindgen_ty_4 = 1; -pub const IORING_REGISTER_FILES: _bindgen_ty_4 = 2; -pub const IORING_UNREGISTER_FILES: _bindgen_ty_4 = 3; -pub const IORING_REGISTER_EVENTFD: _bindgen_ty_4 = 4; -pub const IORING_UNREGISTER_EVENTFD: _bindgen_ty_4 = 5; -pub const IORING_REGISTER_FILES_UPDATE: _bindgen_ty_4 = 6; -pub const IORING_REGISTER_EVENTFD_ASYNC: _bindgen_ty_4 = 7; -pub const IORING_REGISTER_PROBE: _bindgen_ty_4 = 8; -pub const IORING_REGISTER_PERSONALITY: _bindgen_ty_4 = 9; -pub const IORING_UNREGISTER_PERSONALITY: _bindgen_ty_4 = 10; -pub const IORING_REGISTER_RESTRICTIONS: _bindgen_ty_4 = 11; -pub const IORING_REGISTER_ENABLE_RINGS: _bindgen_ty_4 = 12; -pub const IORING_REGISTER_FILES2: _bindgen_ty_4 = 13; -pub const IORING_REGISTER_FILES_UPDATE2: _bindgen_ty_4 = 14; -pub const IORING_REGISTER_BUFFERS2: _bindgen_ty_4 = 15; -pub const IORING_REGISTER_BUFFERS_UPDATE: _bindgen_ty_4 = 16; -pub const IORING_REGISTER_IOWQ_AFF: _bindgen_ty_4 = 17; -pub const IORING_UNREGISTER_IOWQ_AFF: _bindgen_ty_4 = 18; -pub const IORING_REGISTER_IOWQ_MAX_WORKERS: _bindgen_ty_4 = 19; -pub const IORING_REGISTER_LAST: _bindgen_ty_4 = 20; -pub type _bindgen_ty_4 = ::std::os::raw::c_uint; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_uring_files_update { - pub offset: __u32, - pub resv: __u32, - pub fds: __u64, -} -#[test] -fn bindgen_test_layout_io_uring_files_update() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(io_uring_files_update)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_files_update)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).offset) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_files_update), - "::", - stringify!(offset) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_uring_files_update), - "::", - stringify!(resv) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).fds) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(io_uring_files_update), - "::", - stringify!(fds) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_uring_rsrc_register { - pub nr: __u32, - pub resv: __u32, - pub resv2: __u64, - pub data: __u64, - pub tags: __u64, -} -#[test] -fn bindgen_test_layout_io_uring_rsrc_register() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 32usize, - concat!("Size of: ", stringify!(io_uring_rsrc_register)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_rsrc_register)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).nr) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_register), - "::", - stringify!(nr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_register), - "::", - stringify!(resv) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv2) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_register), - "::", - stringify!(resv2) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).data) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_register), - "::", - stringify!(data) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).tags) as usize - ptr as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_register), - "::", - stringify!(tags) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_uring_rsrc_update { - pub offset: __u32, - pub resv: __u32, - pub data: __u64, -} -#[test] -fn bindgen_test_layout_io_uring_rsrc_update() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(io_uring_rsrc_update)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_rsrc_update)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).offset) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_update), - "::", - stringify!(offset) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_update), - "::", - stringify!(resv) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).data) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_update), - "::", - stringify!(data) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_uring_rsrc_update2 { - pub offset: __u32, - pub resv: __u32, - pub data: __u64, - pub tags: __u64, - pub nr: __u32, - pub resv2: __u32, -} -#[test] -fn bindgen_test_layout_io_uring_rsrc_update2() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 32usize, - concat!("Size of: ", stringify!(io_uring_rsrc_update2)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_rsrc_update2)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).offset) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_update2), - "::", - stringify!(offset) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_update2), - "::", - stringify!(resv) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).data) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_update2), - "::", - stringify!(data) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).tags) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_update2), - "::", - stringify!(tags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).nr) as usize - ptr as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_update2), - "::", - stringify!(nr) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv2) as usize - ptr as usize }, - 28usize, - concat!( - "Offset of field: ", - stringify!(io_uring_rsrc_update2), - "::", - stringify!(resv2) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_uring_probe_op { - pub op: __u8, - pub resv: __u8, - pub flags: __u16, - pub resv2: __u32, -} -#[test] -fn bindgen_test_layout_io_uring_probe_op() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(io_uring_probe_op)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(io_uring_probe_op)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).op) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_probe_op), - "::", - stringify!(op) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(io_uring_probe_op), - "::", - stringify!(resv) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(io_uring_probe_op), - "::", - stringify!(flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv2) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_uring_probe_op), - "::", - stringify!(resv2) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default)] -pub struct io_uring_probe { - pub last_op: __u8, - pub ops_len: __u8, - pub resv: __u16, - pub resv2: [__u32; 3usize], - pub ops: __IncompleteArrayField, -} -#[test] -fn bindgen_test_layout_io_uring_probe() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(io_uring_probe)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(io_uring_probe)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).last_op) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_probe), - "::", - stringify!(last_op) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ops_len) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(io_uring_probe), - "::", - stringify!(ops_len) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(io_uring_probe), - "::", - stringify!(resv) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv2) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_uring_probe), - "::", - stringify!(resv2) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ops) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(io_uring_probe), - "::", - stringify!(ops) - ) - ); -} -#[repr(C)] -#[derive(Copy, Clone)] -pub struct io_uring_restriction { - pub opcode: __u16, - pub __bindgen_anon_1: io_uring_restriction__bindgen_ty_1, - pub resv: __u8, - pub resv2: [__u32; 3usize], -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union io_uring_restriction__bindgen_ty_1 { - pub register_op: __u8, - pub sqe_op: __u8, - pub sqe_flags: __u8, -} -#[test] -fn bindgen_test_layout_io_uring_restriction__bindgen_ty_1() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 1usize, - concat!("Size of: ", stringify!(io_uring_restriction__bindgen_ty_1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 1usize, - concat!( - "Alignment of ", - stringify!(io_uring_restriction__bindgen_ty_1) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).register_op) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_restriction__bindgen_ty_1), - "::", - stringify!(register_op) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sqe_op) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_restriction__bindgen_ty_1), - "::", - stringify!(sqe_op) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sqe_flags) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_restriction__bindgen_ty_1), - "::", - stringify!(sqe_flags) - ) - ); -} -impl Default for io_uring_restriction__bindgen_ty_1 { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -#[test] -fn bindgen_test_layout_io_uring_restriction() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(io_uring_restriction)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(io_uring_restriction)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).opcode) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_restriction), - "::", - stringify!(opcode) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv) as usize - ptr as usize }, - 3usize, - concat!( - "Offset of field: ", - stringify!(io_uring_restriction), - "::", - stringify!(resv) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).resv2) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(io_uring_restriction), - "::", - stringify!(resv2) - ) - ); -} -impl Default for io_uring_restriction { - fn default() -> Self { - let mut s = ::std::mem::MaybeUninit::::uninit(); - unsafe { - ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); - s.assume_init() - } - } -} -pub const IORING_RESTRICTION_REGISTER_OP: _bindgen_ty_6 = 0; -pub const IORING_RESTRICTION_SQE_OP: _bindgen_ty_6 = 1; -pub const IORING_RESTRICTION_SQE_FLAGS_ALLOWED: _bindgen_ty_6 = 2; -pub const IORING_RESTRICTION_SQE_FLAGS_REQUIRED: _bindgen_ty_6 = 3; -pub const IORING_RESTRICTION_LAST: _bindgen_ty_6 = 4; -pub type _bindgen_ty_6 = ::std::os::raw::c_uint; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub struct io_uring_getevents_arg { - pub sigmask: __u64, - pub sigmask_sz: __u32, - pub pad: __u32, - pub ts: __u64, -} -#[test] -fn bindgen_test_layout_io_uring_getevents_arg() { - const UNINIT: ::std::mem::MaybeUninit = - ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 24usize, - concat!("Size of: ", stringify!(io_uring_getevents_arg)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(io_uring_getevents_arg)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sigmask) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(io_uring_getevents_arg), - "::", - stringify!(sigmask) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sigmask_sz) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(io_uring_getevents_arg), - "::", - stringify!(sigmask_sz) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pad) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(io_uring_getevents_arg), - "::", - stringify!(pad) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).ts) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(io_uring_getevents_arg), - "::", - stringify!(ts) - ) - ); -} diff --git a/src/vmm/src/io_uring/generated.rs b/src/vmm/src/io_uring/generated.rs new file mode 100644 index 00000000000..445f4a98237 --- /dev/null +++ b/src/vmm/src/io_uring/generated.rs @@ -0,0 +1,728 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// automatically generated by tools/bindgen.sh + +#![allow( + non_camel_case_types, + non_upper_case_globals, + dead_code, + non_snake_case, + clippy::ptr_as_ptr, + clippy::undocumented_unsafe_blocks, + missing_debug_implementations, + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn +)] + +#[repr(C)] +#[derive(Default)] +pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); +impl __IncompleteArrayField { + #[inline] + pub const fn new() -> Self { + __IncompleteArrayField(::std::marker::PhantomData, []) + } + #[inline] + pub fn as_ptr(&self) -> *const T { + self as *const _ as *const T + } + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self as *mut _ as *mut T + } + #[inline] + pub unsafe fn as_slice(&self, len: usize) -> &[T] { + ::std::slice::from_raw_parts(self.as_ptr(), len) + } + #[inline] + pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { + ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + } +} +impl ::std::fmt::Debug for __IncompleteArrayField { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + fmt.write_str("__IncompleteArrayField") + } +} +pub const IORING_SETUP_IOPOLL: u32 = 1; +pub const IORING_SETUP_SQPOLL: u32 = 2; +pub const IORING_SETUP_SQ_AFF: u32 = 4; +pub const IORING_SETUP_CQSIZE: u32 = 8; +pub const IORING_SETUP_CLAMP: u32 = 16; +pub const IORING_SETUP_ATTACH_WQ: u32 = 32; +pub const IORING_SETUP_R_DISABLED: u32 = 64; +pub const IORING_FSYNC_DATASYNC: u32 = 1; +pub const IORING_TIMEOUT_ABS: u32 = 1; +pub const IORING_TIMEOUT_UPDATE: u32 = 2; +pub const IORING_TIMEOUT_BOOTTIME: u32 = 4; +pub const IORING_TIMEOUT_REALTIME: u32 = 8; +pub const IORING_LINK_TIMEOUT_UPDATE: u32 = 16; +pub const IORING_TIMEOUT_CLOCK_MASK: u32 = 12; +pub const IORING_TIMEOUT_UPDATE_MASK: u32 = 18; +pub const IORING_POLL_ADD_MULTI: u32 = 1; +pub const IORING_POLL_UPDATE_EVENTS: u32 = 2; +pub const IORING_POLL_UPDATE_USER_DATA: u32 = 4; +pub const IORING_CQE_F_BUFFER: u32 = 1; +pub const IORING_CQE_F_MORE: u32 = 2; +pub const IORING_OFF_SQ_RING: u32 = 0; +pub const IORING_OFF_CQ_RING: u32 = 134217728; +pub const IORING_OFF_SQES: u32 = 268435456; +pub const IORING_SQ_NEED_WAKEUP: u32 = 1; +pub const IORING_SQ_CQ_OVERFLOW: u32 = 2; +pub const IORING_CQ_EVENTFD_DISABLED: u32 = 1; +pub const IORING_ENTER_GETEVENTS: u32 = 1; +pub const IORING_ENTER_SQ_WAKEUP: u32 = 2; +pub const IORING_ENTER_SQ_WAIT: u32 = 4; +pub const IORING_ENTER_EXT_ARG: u32 = 8; +pub const IORING_FEAT_SINGLE_MMAP: u32 = 1; +pub const IORING_FEAT_NODROP: u32 = 2; +pub const IORING_FEAT_SUBMIT_STABLE: u32 = 4; +pub const IORING_FEAT_RW_CUR_POS: u32 = 8; +pub const IORING_FEAT_CUR_PERSONALITY: u32 = 16; +pub const IORING_FEAT_FAST_POLL: u32 = 32; +pub const IORING_FEAT_POLL_32BITS: u32 = 64; +pub const IORING_FEAT_SQPOLL_NONFIXED: u32 = 128; +pub const IORING_FEAT_EXT_ARG: u32 = 256; +pub const IORING_FEAT_NATIVE_WORKERS: u32 = 512; +pub const IORING_FEAT_RSRC_TAGS: u32 = 1024; +pub const IORING_REGISTER_FILES_SKIP: i32 = -2; +pub const IO_URING_OP_SUPPORTED: u32 = 1; +pub type __u8 = ::std::os::raw::c_uchar; +pub type __u16 = ::std::os::raw::c_ushort; +pub type __s32 = ::std::os::raw::c_int; +pub type __u32 = ::std::os::raw::c_uint; +pub type __u64 = ::std::os::raw::c_ulonglong; +pub type __kernel_rwf_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct io_uring_sqe { + pub opcode: __u8, + pub flags: __u8, + pub ioprio: __u16, + pub fd: __s32, + pub __bindgen_anon_1: io_uring_sqe__bindgen_ty_1, + pub __bindgen_anon_2: io_uring_sqe__bindgen_ty_2, + pub len: __u32, + pub __bindgen_anon_3: io_uring_sqe__bindgen_ty_3, + pub user_data: __u64, + pub __bindgen_anon_4: io_uring_sqe__bindgen_ty_4, + pub personality: __u16, + pub __bindgen_anon_5: io_uring_sqe__bindgen_ty_5, + pub __pad2: [__u64; 2usize], +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union io_uring_sqe__bindgen_ty_1 { + pub off: __u64, + pub addr2: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_sqe__bindgen_ty_1"] + [::std::mem::size_of::() - 8usize]; + ["Alignment of io_uring_sqe__bindgen_ty_1"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_1::off"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_1, off) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_1::addr2"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_1, addr2) - 0usize]; +}; +impl Default for io_uring_sqe__bindgen_ty_1 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union io_uring_sqe__bindgen_ty_2 { + pub addr: __u64, + pub splice_off_in: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_sqe__bindgen_ty_2"] + [::std::mem::size_of::() - 8usize]; + ["Alignment of io_uring_sqe__bindgen_ty_2"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_2::addr"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_2, addr) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_2::splice_off_in"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_2, splice_off_in) - 0usize]; +}; +impl Default for io_uring_sqe__bindgen_ty_2 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union io_uring_sqe__bindgen_ty_3 { + pub rw_flags: __kernel_rwf_t, + pub fsync_flags: __u32, + pub poll_events: __u16, + pub poll32_events: __u32, + pub sync_range_flags: __u32, + pub msg_flags: __u32, + pub timeout_flags: __u32, + pub accept_flags: __u32, + pub cancel_flags: __u32, + pub open_flags: __u32, + pub statx_flags: __u32, + pub fadvise_advice: __u32, + pub splice_flags: __u32, + pub rename_flags: __u32, + pub unlink_flags: __u32, + pub hardlink_flags: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_sqe__bindgen_ty_3"] + [::std::mem::size_of::() - 4usize]; + ["Alignment of io_uring_sqe__bindgen_ty_3"] + [::std::mem::align_of::() - 4usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::rw_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, rw_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::fsync_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, fsync_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::poll_events"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, poll_events) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::poll32_events"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, poll32_events) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::sync_range_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, sync_range_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::msg_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, msg_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::timeout_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, timeout_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::accept_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, accept_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::cancel_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, cancel_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::open_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, open_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::statx_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, statx_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::fadvise_advice"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, fadvise_advice) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::splice_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, splice_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::rename_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, rename_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::unlink_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, unlink_flags) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_3::hardlink_flags"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_3, hardlink_flags) - 0usize]; +}; +impl Default for io_uring_sqe__bindgen_ty_3 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub union io_uring_sqe__bindgen_ty_4 { + pub buf_index: __u16, + pub buf_group: __u16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_sqe__bindgen_ty_4"] + [::std::mem::size_of::() - 2usize]; + ["Alignment of io_uring_sqe__bindgen_ty_4"] + [::std::mem::align_of::() - 1usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_4::buf_index"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_4, buf_index) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_4::buf_group"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_4, buf_group) - 0usize]; +}; +impl Default for io_uring_sqe__bindgen_ty_4 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union io_uring_sqe__bindgen_ty_5 { + pub splice_fd_in: __s32, + pub file_index: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_sqe__bindgen_ty_5"] + [::std::mem::size_of::() - 4usize]; + ["Alignment of io_uring_sqe__bindgen_ty_5"] + [::std::mem::align_of::() - 4usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_5::splice_fd_in"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_5, splice_fd_in) - 0usize]; + ["Offset of field: io_uring_sqe__bindgen_ty_5::file_index"] + [::std::mem::offset_of!(io_uring_sqe__bindgen_ty_5, file_index) - 0usize]; +}; +impl Default for io_uring_sqe__bindgen_ty_5 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_sqe"][::std::mem::size_of::() - 64usize]; + ["Alignment of io_uring_sqe"][::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_sqe::opcode"] + [::std::mem::offset_of!(io_uring_sqe, opcode) - 0usize]; + ["Offset of field: io_uring_sqe::flags"][::std::mem::offset_of!(io_uring_sqe, flags) - 1usize]; + ["Offset of field: io_uring_sqe::ioprio"] + [::std::mem::offset_of!(io_uring_sqe, ioprio) - 2usize]; + ["Offset of field: io_uring_sqe::fd"][::std::mem::offset_of!(io_uring_sqe, fd) - 4usize]; + ["Offset of field: io_uring_sqe::len"][::std::mem::offset_of!(io_uring_sqe, len) - 24usize]; + ["Offset of field: io_uring_sqe::user_data"] + [::std::mem::offset_of!(io_uring_sqe, user_data) - 32usize]; + ["Offset of field: io_uring_sqe::personality"] + [::std::mem::offset_of!(io_uring_sqe, personality) - 42usize]; + ["Offset of field: io_uring_sqe::__pad2"] + [::std::mem::offset_of!(io_uring_sqe, __pad2) - 48usize]; +}; +impl Default for io_uring_sqe { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +pub const IOSQE_FIXED_FILE_BIT: _bindgen_ty_1 = 0; +pub const IOSQE_IO_DRAIN_BIT: _bindgen_ty_1 = 1; +pub const IOSQE_IO_LINK_BIT: _bindgen_ty_1 = 2; +pub const IOSQE_IO_HARDLINK_BIT: _bindgen_ty_1 = 3; +pub const IOSQE_ASYNC_BIT: _bindgen_ty_1 = 4; +pub const IOSQE_BUFFER_SELECT_BIT: _bindgen_ty_1 = 5; +pub type _bindgen_ty_1 = ::std::os::raw::c_uint; +pub const IORING_OP_NOP: _bindgen_ty_2 = 0; +pub const IORING_OP_READV: _bindgen_ty_2 = 1; +pub const IORING_OP_WRITEV: _bindgen_ty_2 = 2; +pub const IORING_OP_FSYNC: _bindgen_ty_2 = 3; +pub const IORING_OP_READ_FIXED: _bindgen_ty_2 = 4; +pub const IORING_OP_WRITE_FIXED: _bindgen_ty_2 = 5; +pub const IORING_OP_POLL_ADD: _bindgen_ty_2 = 6; +pub const IORING_OP_POLL_REMOVE: _bindgen_ty_2 = 7; +pub const IORING_OP_SYNC_FILE_RANGE: _bindgen_ty_2 = 8; +pub const IORING_OP_SENDMSG: _bindgen_ty_2 = 9; +pub const IORING_OP_RECVMSG: _bindgen_ty_2 = 10; +pub const IORING_OP_TIMEOUT: _bindgen_ty_2 = 11; +pub const IORING_OP_TIMEOUT_REMOVE: _bindgen_ty_2 = 12; +pub const IORING_OP_ACCEPT: _bindgen_ty_2 = 13; +pub const IORING_OP_ASYNC_CANCEL: _bindgen_ty_2 = 14; +pub const IORING_OP_LINK_TIMEOUT: _bindgen_ty_2 = 15; +pub const IORING_OP_CONNECT: _bindgen_ty_2 = 16; +pub const IORING_OP_FALLOCATE: _bindgen_ty_2 = 17; +pub const IORING_OP_OPENAT: _bindgen_ty_2 = 18; +pub const IORING_OP_CLOSE: _bindgen_ty_2 = 19; +pub const IORING_OP_FILES_UPDATE: _bindgen_ty_2 = 20; +pub const IORING_OP_STATX: _bindgen_ty_2 = 21; +pub const IORING_OP_READ: _bindgen_ty_2 = 22; +pub const IORING_OP_WRITE: _bindgen_ty_2 = 23; +pub const IORING_OP_FADVISE: _bindgen_ty_2 = 24; +pub const IORING_OP_MADVISE: _bindgen_ty_2 = 25; +pub const IORING_OP_SEND: _bindgen_ty_2 = 26; +pub const IORING_OP_RECV: _bindgen_ty_2 = 27; +pub const IORING_OP_OPENAT2: _bindgen_ty_2 = 28; +pub const IORING_OP_EPOLL_CTL: _bindgen_ty_2 = 29; +pub const IORING_OP_SPLICE: _bindgen_ty_2 = 30; +pub const IORING_OP_PROVIDE_BUFFERS: _bindgen_ty_2 = 31; +pub const IORING_OP_REMOVE_BUFFERS: _bindgen_ty_2 = 32; +pub const IORING_OP_TEE: _bindgen_ty_2 = 33; +pub const IORING_OP_SHUTDOWN: _bindgen_ty_2 = 34; +pub const IORING_OP_RENAMEAT: _bindgen_ty_2 = 35; +pub const IORING_OP_UNLINKAT: _bindgen_ty_2 = 36; +pub const IORING_OP_LAST: _bindgen_ty_2 = 37; +pub type _bindgen_ty_2 = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_uring_cqe { + pub user_data: __u64, + pub res: __s32, + pub flags: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_cqe"][::std::mem::size_of::() - 16usize]; + ["Alignment of io_uring_cqe"][::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_cqe::user_data"] + [::std::mem::offset_of!(io_uring_cqe, user_data) - 0usize]; + ["Offset of field: io_uring_cqe::res"][::std::mem::offset_of!(io_uring_cqe, res) - 8usize]; + ["Offset of field: io_uring_cqe::flags"][::std::mem::offset_of!(io_uring_cqe, flags) - 12usize]; +}; +pub const IORING_CQE_BUFFER_SHIFT: _bindgen_ty_3 = 16; +pub type _bindgen_ty_3 = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_sqring_offsets { + pub head: __u32, + pub tail: __u32, + pub ring_mask: __u32, + pub ring_entries: __u32, + pub flags: __u32, + pub dropped: __u32, + pub array: __u32, + pub resv1: __u32, + pub resv2: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_sqring_offsets"][::std::mem::size_of::() - 40usize]; + ["Alignment of io_sqring_offsets"][::std::mem::align_of::() - 8usize]; + ["Offset of field: io_sqring_offsets::head"] + [::std::mem::offset_of!(io_sqring_offsets, head) - 0usize]; + ["Offset of field: io_sqring_offsets::tail"] + [::std::mem::offset_of!(io_sqring_offsets, tail) - 4usize]; + ["Offset of field: io_sqring_offsets::ring_mask"] + [::std::mem::offset_of!(io_sqring_offsets, ring_mask) - 8usize]; + ["Offset of field: io_sqring_offsets::ring_entries"] + [::std::mem::offset_of!(io_sqring_offsets, ring_entries) - 12usize]; + ["Offset of field: io_sqring_offsets::flags"] + [::std::mem::offset_of!(io_sqring_offsets, flags) - 16usize]; + ["Offset of field: io_sqring_offsets::dropped"] + [::std::mem::offset_of!(io_sqring_offsets, dropped) - 20usize]; + ["Offset of field: io_sqring_offsets::array"] + [::std::mem::offset_of!(io_sqring_offsets, array) - 24usize]; + ["Offset of field: io_sqring_offsets::resv1"] + [::std::mem::offset_of!(io_sqring_offsets, resv1) - 28usize]; + ["Offset of field: io_sqring_offsets::resv2"] + [::std::mem::offset_of!(io_sqring_offsets, resv2) - 32usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_cqring_offsets { + pub head: __u32, + pub tail: __u32, + pub ring_mask: __u32, + pub ring_entries: __u32, + pub overflow: __u32, + pub cqes: __u32, + pub flags: __u32, + pub resv1: __u32, + pub resv2: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_cqring_offsets"][::std::mem::size_of::() - 40usize]; + ["Alignment of io_cqring_offsets"][::std::mem::align_of::() - 8usize]; + ["Offset of field: io_cqring_offsets::head"] + [::std::mem::offset_of!(io_cqring_offsets, head) - 0usize]; + ["Offset of field: io_cqring_offsets::tail"] + [::std::mem::offset_of!(io_cqring_offsets, tail) - 4usize]; + ["Offset of field: io_cqring_offsets::ring_mask"] + [::std::mem::offset_of!(io_cqring_offsets, ring_mask) - 8usize]; + ["Offset of field: io_cqring_offsets::ring_entries"] + [::std::mem::offset_of!(io_cqring_offsets, ring_entries) - 12usize]; + ["Offset of field: io_cqring_offsets::overflow"] + [::std::mem::offset_of!(io_cqring_offsets, overflow) - 16usize]; + ["Offset of field: io_cqring_offsets::cqes"] + [::std::mem::offset_of!(io_cqring_offsets, cqes) - 20usize]; + ["Offset of field: io_cqring_offsets::flags"] + [::std::mem::offset_of!(io_cqring_offsets, flags) - 24usize]; + ["Offset of field: io_cqring_offsets::resv1"] + [::std::mem::offset_of!(io_cqring_offsets, resv1) - 28usize]; + ["Offset of field: io_cqring_offsets::resv2"] + [::std::mem::offset_of!(io_cqring_offsets, resv2) - 32usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_uring_params { + pub sq_entries: __u32, + pub cq_entries: __u32, + pub flags: __u32, + pub sq_thread_cpu: __u32, + pub sq_thread_idle: __u32, + pub features: __u32, + pub wq_fd: __u32, + pub resv: [__u32; 3usize], + pub sq_off: io_sqring_offsets, + pub cq_off: io_cqring_offsets, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_params"][::std::mem::size_of::() - 120usize]; + ["Alignment of io_uring_params"][::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_params::sq_entries"] + [::std::mem::offset_of!(io_uring_params, sq_entries) - 0usize]; + ["Offset of field: io_uring_params::cq_entries"] + [::std::mem::offset_of!(io_uring_params, cq_entries) - 4usize]; + ["Offset of field: io_uring_params::flags"] + [::std::mem::offset_of!(io_uring_params, flags) - 8usize]; + ["Offset of field: io_uring_params::sq_thread_cpu"] + [::std::mem::offset_of!(io_uring_params, sq_thread_cpu) - 12usize]; + ["Offset of field: io_uring_params::sq_thread_idle"] + [::std::mem::offset_of!(io_uring_params, sq_thread_idle) - 16usize]; + ["Offset of field: io_uring_params::features"] + [::std::mem::offset_of!(io_uring_params, features) - 20usize]; + ["Offset of field: io_uring_params::wq_fd"] + [::std::mem::offset_of!(io_uring_params, wq_fd) - 24usize]; + ["Offset of field: io_uring_params::resv"] + [::std::mem::offset_of!(io_uring_params, resv) - 28usize]; + ["Offset of field: io_uring_params::sq_off"] + [::std::mem::offset_of!(io_uring_params, sq_off) - 40usize]; + ["Offset of field: io_uring_params::cq_off"] + [::std::mem::offset_of!(io_uring_params, cq_off) - 80usize]; +}; +pub const IORING_REGISTER_BUFFERS: _bindgen_ty_4 = 0; +pub const IORING_UNREGISTER_BUFFERS: _bindgen_ty_4 = 1; +pub const IORING_REGISTER_FILES: _bindgen_ty_4 = 2; +pub const IORING_UNREGISTER_FILES: _bindgen_ty_4 = 3; +pub const IORING_REGISTER_EVENTFD: _bindgen_ty_4 = 4; +pub const IORING_UNREGISTER_EVENTFD: _bindgen_ty_4 = 5; +pub const IORING_REGISTER_FILES_UPDATE: _bindgen_ty_4 = 6; +pub const IORING_REGISTER_EVENTFD_ASYNC: _bindgen_ty_4 = 7; +pub const IORING_REGISTER_PROBE: _bindgen_ty_4 = 8; +pub const IORING_REGISTER_PERSONALITY: _bindgen_ty_4 = 9; +pub const IORING_UNREGISTER_PERSONALITY: _bindgen_ty_4 = 10; +pub const IORING_REGISTER_RESTRICTIONS: _bindgen_ty_4 = 11; +pub const IORING_REGISTER_ENABLE_RINGS: _bindgen_ty_4 = 12; +pub const IORING_REGISTER_FILES2: _bindgen_ty_4 = 13; +pub const IORING_REGISTER_FILES_UPDATE2: _bindgen_ty_4 = 14; +pub const IORING_REGISTER_BUFFERS2: _bindgen_ty_4 = 15; +pub const IORING_REGISTER_BUFFERS_UPDATE: _bindgen_ty_4 = 16; +pub const IORING_REGISTER_IOWQ_AFF: _bindgen_ty_4 = 17; +pub const IORING_UNREGISTER_IOWQ_AFF: _bindgen_ty_4 = 18; +pub const IORING_REGISTER_IOWQ_MAX_WORKERS: _bindgen_ty_4 = 19; +pub const IORING_REGISTER_LAST: _bindgen_ty_4 = 20; +pub type _bindgen_ty_4 = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_uring_files_update { + pub offset: __u32, + pub resv: __u32, + pub fds: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_files_update"][::std::mem::size_of::() - 16usize]; + ["Alignment of io_uring_files_update"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_files_update::offset"] + [::std::mem::offset_of!(io_uring_files_update, offset) - 0usize]; + ["Offset of field: io_uring_files_update::resv"] + [::std::mem::offset_of!(io_uring_files_update, resv) - 4usize]; + ["Offset of field: io_uring_files_update::fds"] + [::std::mem::offset_of!(io_uring_files_update, fds) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_uring_rsrc_register { + pub nr: __u32, + pub resv: __u32, + pub resv2: __u64, + pub data: __u64, + pub tags: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_rsrc_register"][::std::mem::size_of::() - 32usize]; + ["Alignment of io_uring_rsrc_register"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_rsrc_register::nr"] + [::std::mem::offset_of!(io_uring_rsrc_register, nr) - 0usize]; + ["Offset of field: io_uring_rsrc_register::resv"] + [::std::mem::offset_of!(io_uring_rsrc_register, resv) - 4usize]; + ["Offset of field: io_uring_rsrc_register::resv2"] + [::std::mem::offset_of!(io_uring_rsrc_register, resv2) - 8usize]; + ["Offset of field: io_uring_rsrc_register::data"] + [::std::mem::offset_of!(io_uring_rsrc_register, data) - 16usize]; + ["Offset of field: io_uring_rsrc_register::tags"] + [::std::mem::offset_of!(io_uring_rsrc_register, tags) - 24usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_uring_rsrc_update { + pub offset: __u32, + pub resv: __u32, + pub data: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_rsrc_update"][::std::mem::size_of::() - 16usize]; + ["Alignment of io_uring_rsrc_update"][::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_rsrc_update::offset"] + [::std::mem::offset_of!(io_uring_rsrc_update, offset) - 0usize]; + ["Offset of field: io_uring_rsrc_update::resv"] + [::std::mem::offset_of!(io_uring_rsrc_update, resv) - 4usize]; + ["Offset of field: io_uring_rsrc_update::data"] + [::std::mem::offset_of!(io_uring_rsrc_update, data) - 8usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_uring_rsrc_update2 { + pub offset: __u32, + pub resv: __u32, + pub data: __u64, + pub tags: __u64, + pub nr: __u32, + pub resv2: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_rsrc_update2"][::std::mem::size_of::() - 32usize]; + ["Alignment of io_uring_rsrc_update2"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_rsrc_update2::offset"] + [::std::mem::offset_of!(io_uring_rsrc_update2, offset) - 0usize]; + ["Offset of field: io_uring_rsrc_update2::resv"] + [::std::mem::offset_of!(io_uring_rsrc_update2, resv) - 4usize]; + ["Offset of field: io_uring_rsrc_update2::data"] + [::std::mem::offset_of!(io_uring_rsrc_update2, data) - 8usize]; + ["Offset of field: io_uring_rsrc_update2::tags"] + [::std::mem::offset_of!(io_uring_rsrc_update2, tags) - 16usize]; + ["Offset of field: io_uring_rsrc_update2::nr"] + [::std::mem::offset_of!(io_uring_rsrc_update2, nr) - 24usize]; + ["Offset of field: io_uring_rsrc_update2::resv2"] + [::std::mem::offset_of!(io_uring_rsrc_update2, resv2) - 28usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_uring_probe_op { + pub op: __u8, + pub resv: __u8, + pub flags: __u16, + pub resv2: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_probe_op"][::std::mem::size_of::() - 8usize]; + ["Alignment of io_uring_probe_op"][::std::mem::align_of::() - 4usize]; + ["Offset of field: io_uring_probe_op::op"] + [::std::mem::offset_of!(io_uring_probe_op, op) - 0usize]; + ["Offset of field: io_uring_probe_op::resv"] + [::std::mem::offset_of!(io_uring_probe_op, resv) - 1usize]; + ["Offset of field: io_uring_probe_op::flags"] + [::std::mem::offset_of!(io_uring_probe_op, flags) - 2usize]; + ["Offset of field: io_uring_probe_op::resv2"] + [::std::mem::offset_of!(io_uring_probe_op, resv2) - 4usize]; +}; +#[repr(C)] +#[derive(Debug, Default)] +pub struct io_uring_probe { + pub last_op: __u8, + pub ops_len: __u8, + pub resv: __u16, + pub resv2: [__u32; 3usize], + pub ops: __IncompleteArrayField, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_probe"][::std::mem::size_of::() - 16usize]; + ["Alignment of io_uring_probe"][::std::mem::align_of::() - 4usize]; + ["Offset of field: io_uring_probe::last_op"] + [::std::mem::offset_of!(io_uring_probe, last_op) - 0usize]; + ["Offset of field: io_uring_probe::ops_len"] + [::std::mem::offset_of!(io_uring_probe, ops_len) - 1usize]; + ["Offset of field: io_uring_probe::resv"] + [::std::mem::offset_of!(io_uring_probe, resv) - 2usize]; + ["Offset of field: io_uring_probe::resv2"] + [::std::mem::offset_of!(io_uring_probe, resv2) - 4usize]; + ["Offset of field: io_uring_probe::ops"][::std::mem::offset_of!(io_uring_probe, ops) - 16usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct io_uring_restriction { + pub opcode: __u16, + pub __bindgen_anon_1: io_uring_restriction__bindgen_ty_1, + pub resv: __u8, + pub resv2: [__u32; 3usize], +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union io_uring_restriction__bindgen_ty_1 { + pub register_op: __u8, + pub sqe_op: __u8, + pub sqe_flags: __u8, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_restriction__bindgen_ty_1"] + [::std::mem::size_of::() - 1usize]; + ["Alignment of io_uring_restriction__bindgen_ty_1"] + [::std::mem::align_of::() - 1usize]; + ["Offset of field: io_uring_restriction__bindgen_ty_1::register_op"] + [::std::mem::offset_of!(io_uring_restriction__bindgen_ty_1, register_op) - 0usize]; + ["Offset of field: io_uring_restriction__bindgen_ty_1::sqe_op"] + [::std::mem::offset_of!(io_uring_restriction__bindgen_ty_1, sqe_op) - 0usize]; + ["Offset of field: io_uring_restriction__bindgen_ty_1::sqe_flags"] + [::std::mem::offset_of!(io_uring_restriction__bindgen_ty_1, sqe_flags) - 0usize]; +}; +impl Default for io_uring_restriction__bindgen_ty_1 { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_restriction"][::std::mem::size_of::() - 16usize]; + ["Alignment of io_uring_restriction"][::std::mem::align_of::() - 4usize]; + ["Offset of field: io_uring_restriction::opcode"] + [::std::mem::offset_of!(io_uring_restriction, opcode) - 0usize]; + ["Offset of field: io_uring_restriction::resv"] + [::std::mem::offset_of!(io_uring_restriction, resv) - 3usize]; + ["Offset of field: io_uring_restriction::resv2"] + [::std::mem::offset_of!(io_uring_restriction, resv2) - 4usize]; +}; +impl Default for io_uring_restriction { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +pub const IORING_RESTRICTION_REGISTER_OP: _bindgen_ty_6 = 0; +pub const IORING_RESTRICTION_SQE_OP: _bindgen_ty_6 = 1; +pub const IORING_RESTRICTION_SQE_FLAGS_ALLOWED: _bindgen_ty_6 = 2; +pub const IORING_RESTRICTION_SQE_FLAGS_REQUIRED: _bindgen_ty_6 = 3; +pub const IORING_RESTRICTION_LAST: _bindgen_ty_6 = 4; +pub type _bindgen_ty_6 = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct io_uring_getevents_arg { + pub sigmask: __u64, + pub sigmask_sz: __u32, + pub pad: __u32, + pub ts: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of io_uring_getevents_arg"][::std::mem::size_of::() - 24usize]; + ["Alignment of io_uring_getevents_arg"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: io_uring_getevents_arg::sigmask"] + [::std::mem::offset_of!(io_uring_getevents_arg, sigmask) - 0usize]; + ["Offset of field: io_uring_getevents_arg::sigmask_sz"] + [::std::mem::offset_of!(io_uring_getevents_arg, sigmask_sz) - 8usize]; + ["Offset of field: io_uring_getevents_arg::pad"] + [::std::mem::offset_of!(io_uring_getevents_arg, pad) - 12usize]; + ["Offset of field: io_uring_getevents_arg::ts"] + [::std::mem::offset_of!(io_uring_getevents_arg, ts) - 16usize]; +}; diff --git a/src/vmm/src/io_uring/mod.rs b/src/vmm/src/io_uring/mod.rs index 3466fd01aa5..1c2ca1bfc6e 100644 --- a/src/vmm/src/io_uring/mod.rs +++ b/src/vmm/src/io_uring/mod.rs @@ -1,8 +1,7 @@ // Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -#[allow(clippy::undocumented_unsafe_blocks)] -mod gen; +mod generated; pub mod operation; mod probe; mod queue; @@ -14,9 +13,9 @@ use std::fs::File; use std::io::Error as IOError; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use gen::io_uring_params; +use generated::io_uring_params; use operation::{Cqe, FixedFd, OpCode, Operation}; -use probe::{ProbeWrapper, PROBE_LEN}; +use probe::{PROBE_LEN, ProbeWrapper}; pub use queue::completion::CQueueError; use queue::completion::CompletionQueue; pub use queue::submission::SQueueError; @@ -97,8 +96,8 @@ impl IoUring { /// /// # Arguments /// - /// * `num_entries` - Requested number of entries in the ring. Will be rounded up to the - /// nearest power of two. + /// * `num_entries` - Requested number of entries in the ring. Will be rounded up to the nearest + /// power of two. /// * `files` - Files to be registered for IO. /// * `restrictions` - Vector of [`Restriction`](restriction/enum.Restriction.html)s /// * `eventfd` - Optional eventfd for receiving completion notifications. @@ -110,7 +109,7 @@ impl IoUring { ) -> Result { let mut params = io_uring_params { // Create the ring as disabled, so that we may register restrictions. - flags: gen::IORING_SETUP_R_DISABLED, + flags: generated::IORING_SETUP_R_DISABLED, ..Default::default() }; @@ -175,10 +174,9 @@ impl IoUring { } self.squeue .push(op.into_sqe(&mut self.slab)) - .map(|res| { + .inspect(|_| { // This is safe since self.num_ops < IORING_MAX_CQ_ENTRIES (65536) self.num_ops += 1; - res }) .map_err(|(sqe_err, user_data_key)| -> (IoUringError, T) { ( @@ -207,11 +205,10 @@ impl IoUring { self.cqueue .pop(&mut self.slab) .map(|maybe_cqe| { - maybe_cqe.map(|cqe| { + maybe_cqe.inspect(|_| { // This is safe since the pop-ed CQEs have been previously pushed. However // we use a saturating_sub for extra safety. self.num_ops = self.num_ops.saturating_sub(1); - cqe }) }) .map_err(IoUringError::CQueue) @@ -250,7 +247,7 @@ impl IoUring { libc::syscall( libc::SYS_io_uring_register, self.fd.as_raw_fd(), - gen::IORING_REGISTER_ENABLE_RINGS, + generated::IORING_REGISTER_ENABLE_RINGS, std::ptr::null::(), 0, ) @@ -275,7 +272,7 @@ impl IoUring { libc::syscall( libc::SYS_io_uring_register, self.fd.as_raw_fd(), - gen::IORING_REGISTER_FILES, + generated::IORING_REGISTER_FILES, files .iter() .map(|f| f.as_raw_fd()) @@ -299,7 +296,7 @@ impl IoUring { libc::syscall( libc::SYS_io_uring_register, self.fd.as_raw_fd(), - gen::IORING_REGISTER_EVENTFD, + generated::IORING_REGISTER_EVENTFD, (&fd) as *const _, 1, ) @@ -318,10 +315,10 @@ impl IoUring { libc::syscall( libc::SYS_io_uring_register, self.fd.as_raw_fd(), - gen::IORING_REGISTER_RESTRICTIONS, + generated::IORING_REGISTER_RESTRICTIONS, restrictions .iter() - .map(gen::io_uring_restriction::from) + .map(generated::io_uring_restriction::from) .collect::>() .as_mut_slice() .as_mut_ptr(), @@ -339,7 +336,7 @@ impl IoUring { // An alternative fix would be to keep an internal counter that tracks the number of // submitted entries that haven't been completed and makes sure it doesn't exceed // (2 * num_entries). - if (params.features & gen::IORING_FEAT_NODROP) == 0 { + if (params.features & generated::IORING_FEAT_NODROP) == 0 { return Err(IoUringError::UnsupportedFeature("IORING_FEAT_NODROP")); } @@ -354,7 +351,7 @@ impl IoUring { libc::syscall( libc::SYS_io_uring_register, self.fd.as_raw_fd(), - gen::IORING_REGISTER_PROBE, + generated::IORING_REGISTER_PROBE, probes.as_mut_fam_struct_ptr(), PROBE_LEN, ) @@ -365,7 +362,7 @@ impl IoUring { let supported_opcodes: HashSet = probes .as_slice() .iter() - .filter(|op| ((u32::from(op.flags)) & gen::IO_URING_OP_SUPPORTED) != 0) + .filter(|op| ((u32::from(op.flags)) & generated::IO_URING_OP_SUPPORTED) != 0) .map(|op| op.op) .collect(); diff --git a/src/vmm/src/io_uring/operation/cqe.rs b/src/vmm/src/io_uring/operation/cqe.rs index 2b4f4165e4c..bd445ecb28a 100644 --- a/src/vmm/src/io_uring/operation/cqe.rs +++ b/src/vmm/src/io_uring/operation/cqe.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; -use crate::io_uring::gen::io_uring_cqe; +use crate::io_uring::generated::io_uring_cqe; use crate::vstate::memory::ByteValued; // SAFETY: Struct is POD and contains no references or niches. diff --git a/src/vmm/src/io_uring/operation/mod.rs b/src/vmm/src/io_uring/operation/mod.rs index dc9cb958b3c..6307d1413c0 100644 --- a/src/vmm/src/io_uring/operation/mod.rs +++ b/src/vmm/src/io_uring/operation/mod.rs @@ -12,7 +12,7 @@ use std::fmt::{self, Debug}; pub use cqe::Cqe; pub(crate) use sqe::Sqe; -use crate::io_uring::gen::{self, io_uring_sqe, IOSQE_FIXED_FILE_BIT}; +use crate::io_uring::generated::{self, IOSQE_FIXED_FILE_BIT, io_uring_sqe}; /// The index of a registered fd. pub type FixedFd = u32; @@ -24,11 +24,11 @@ pub type FixedFd = u32; /// Supported operation types. pub enum OpCode { /// Read operation. - Read = gen::IORING_OP_READ as u8, + Read = generated::IORING_OP_READ as u8, /// Write operation. - Write = gen::IORING_OP_WRITE as u8, + Write = generated::IORING_OP_WRITE as u8, /// Fsync operation. - Fsync = gen::IORING_OP_FSYNC as u8, + Fsync = generated::IORING_OP_FSYNC as u8, } // Useful for outputting errors. @@ -120,7 +120,7 @@ impl Operation { // Needed for proptesting. #[cfg(test)] pub(crate) fn set_linked(&mut self) { - self.flags |= 1 << gen::IOSQE_IO_LINK_BIT; + self.flags |= 1 << generated::IOSQE_IO_LINK_BIT; } /// Transform the operation into an `Sqe`. diff --git a/src/vmm/src/io_uring/operation/sqe.rs b/src/vmm/src/io_uring/operation/sqe.rs index f405becd411..93c3be32d4c 100644 --- a/src/vmm/src/io_uring/operation/sqe.rs +++ b/src/vmm/src/io_uring/operation/sqe.rs @@ -3,7 +3,7 @@ use std::fmt::{self}; -use crate::io_uring::gen::io_uring_sqe; +use crate::io_uring::generated::io_uring_sqe; use crate::vstate::memory::ByteValued; // SAFETY: Struct is POD and contains no references or niches. diff --git a/src/vmm/src/io_uring/probe.rs b/src/vmm/src/io_uring/probe.rs index a1f74385815..4ac68daacef 100644 --- a/src/vmm/src/io_uring/probe.rs +++ b/src/vmm/src/io_uring/probe.rs @@ -4,7 +4,7 @@ use vmm_sys_util::fam::{FamStruct, FamStructWrapper}; use vmm_sys_util::generate_fam_struct_impl; -use crate::io_uring::gen::{io_uring_probe, io_uring_probe_op}; +use crate::io_uring::generated::{io_uring_probe, io_uring_probe_op}; // There is no max for the number of operations returned by probing. So we fallback to using the // number of values representable in a u8; diff --git a/src/vmm/src/io_uring/queue/completion.rs b/src/vmm/src/io_uring/queue/completion.rs index 106229d032b..1af8ef3ed71 100644 --- a/src/vmm/src/io_uring/queue/completion.rs +++ b/src/vmm/src/io_uring/queue/completion.rs @@ -8,8 +8,8 @@ use std::sync::atomic::Ordering; use vm_memory::{Bytes, VolatileMemory, VolatileMemoryError}; -use super::mmap::{mmap, MmapError}; -use crate::io_uring::gen; +use super::mmap::{MmapError, mmap}; +use crate::io_uring::generated; use crate::io_uring::operation::Cqe; use crate::vstate::memory::MmapRegion; @@ -43,15 +43,15 @@ pub(crate) struct CompletionQueue { impl CompletionQueue { pub(crate) fn new( io_uring_fd: RawFd, - params: &gen::io_uring_params, + params: &generated::io_uring_params, ) -> Result { let offsets = params.cq_off; // Map the CQ_ring. The actual size of the ring is `num_entries * size_of(entry_type)`. // To this we add an offset as per the io_uring specifications. let ring_size = (params.cq_off.cqes as usize) - + (params.cq_entries as usize) * std::mem::size_of::(); - let cqes = mmap(ring_size, io_uring_fd, gen::IORING_OFF_CQ_RING.into())?; + + (params.cq_entries as usize) * std::mem::size_of::(); + let cqes = mmap(ring_size, io_uring_fd, generated::IORING_OFF_CQ_RING.into())?; let ring = cqes.as_volatile_slice(); let ring_mask = ring.read_obj(offsets.ring_mask as usize)?; @@ -86,8 +86,8 @@ impl CompletionQueue { // validate that we have smth to fetch if Wrapping(unmasked_tail) - self.unmasked_head > Wrapping(0) { - let cqe: gen::io_uring_cqe = ring.read_obj( - self.cqes_off + (head as usize) * std::mem::size_of::(), + let cqe: generated::io_uring_cqe = ring.read_obj( + self.cqes_off + (head as usize) * std::mem::size_of::(), )?; // increase the head diff --git a/src/vmm/src/io_uring/queue/submission.rs b/src/vmm/src/io_uring/queue/submission.rs index c8306b40d26..db308e63a54 100644 --- a/src/vmm/src/io_uring/queue/submission.rs +++ b/src/vmm/src/io_uring/queue/submission.rs @@ -11,8 +11,8 @@ use std::sync::atomic::Ordering; use vm_memory::{VolatileMemory, VolatileMemoryError}; use vmm_sys_util::syscall::SyscallReturnCode; -use super::mmap::{mmap, MmapError}; -use crate::io_uring::gen; +use super::mmap::{MmapError, mmap}; +use crate::io_uring::generated; use crate::io_uring::operation::Sqe; use crate::vstate::memory::{Bytes, MmapRegion}; @@ -54,7 +54,7 @@ pub(crate) struct SubmissionQueue { impl SubmissionQueue { pub(crate) fn new( io_uring_fd: RawFd, - params: &gen::io_uring_params, + params: &generated::io_uring_params, ) -> Result { let (ring, sqes) = Self::mmap(io_uring_fd, params)?; let ring_slice = ring.as_volatile_slice(); @@ -99,11 +99,10 @@ impl SubmissionQueue { } // retrieve and populate the sqe - if let Err(err) = self - .sqes - .as_volatile_slice() - .write_obj(sqe.0, (tail as usize) * mem::size_of::()) - { + if let Err(err) = self.sqes.as_volatile_slice().write_obj( + sqe.0, + (tail as usize) * mem::size_of::(), + ) { return Err((SQueueError::VolatileMemory(err), sqe.user_data())); } @@ -129,7 +128,7 @@ impl SubmissionQueue { let mut flags = 0; if min_complete > 0 { - flags |= gen::IORING_ENTER_GETEVENTS; + flags |= generated::IORING_ENTER_GETEVENTS; } // SAFETY: Safe because values are valid and we check the return value. let submitted = SyscallReturnCode(unsafe { @@ -155,19 +154,28 @@ impl SubmissionQueue { fn mmap( io_uring_fd: RawFd, - params: &gen::io_uring_params, + params: &generated::io_uring_params, ) -> Result<(MmapRegion, MmapRegion), SQueueError> { // map the SQ_ring. The actual size of the ring is `num_entries * size_of(entry_type)`. // To this we add an offset as per the io_uring specifications. let sqe_ring_size = (params.sq_off.array as usize) + (params.sq_entries as usize) * mem::size_of::(); - let sqe_ring = mmap(sqe_ring_size, io_uring_fd, gen::IORING_OFF_SQ_RING.into())?; + let sqe_ring = mmap( + sqe_ring_size, + io_uring_fd, + generated::IORING_OFF_SQ_RING.into(), + )?; // map the SQEs. - let sqes_array_size = (params.sq_entries as usize) * mem::size_of::(); + let sqes_array_size = + (params.sq_entries as usize) * mem::size_of::(); - let sqes = mmap(sqes_array_size, io_uring_fd, gen::IORING_OFF_SQES.into())?; + let sqes = mmap( + sqes_array_size, + io_uring_fd, + generated::IORING_OFF_SQES.into(), + )?; Ok((sqe_ring, sqes)) } diff --git a/src/vmm/src/io_uring/restriction.rs b/src/vmm/src/io_uring/restriction.rs index f53478117df..54b8f5eefc8 100644 --- a/src/vmm/src/io_uring/restriction.rs +++ b/src/vmm/src/io_uring/restriction.rs @@ -12,7 +12,7 @@ use std::convert::From; -use crate::io_uring::gen; +use crate::io_uring::generated; use crate::io_uring::operation::OpCode; /// Adds support for restricting the operations allowed by io_uring. @@ -24,7 +24,7 @@ pub enum Restriction { RequireFixedFds, } -impl From<&Restriction> for gen::io_uring_restriction { +impl From<&Restriction> for generated::io_uring_restriction { fn from(restriction: &Restriction) -> Self { use Restriction::*; @@ -33,13 +33,13 @@ impl From<&Restriction> for gen::io_uring_restriction { match restriction { AllowOpCode(opcode) => { - instance.opcode = u16::try_from(gen::IORING_RESTRICTION_SQE_OP).unwrap(); + instance.opcode = u16::try_from(generated::IORING_RESTRICTION_SQE_OP).unwrap(); instance.__bindgen_anon_1.sqe_op = *opcode as u8; } RequireFixedFds => { instance.opcode = - u16::try_from(gen::IORING_RESTRICTION_SQE_FLAGS_REQUIRED).unwrap(); - instance.__bindgen_anon_1.sqe_flags = 1 << gen::IOSQE_FIXED_FILE_BIT; + u16::try_from(generated::IORING_RESTRICTION_SQE_FLAGS_REQUIRED).unwrap(); + instance.__bindgen_anon_1.sqe_flags = 1 << generated::IOSQE_FIXED_FILE_BIT; } }; diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index c80f004e789..29f3b0148ac 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -97,7 +97,7 @@ pub mod resources; /// microVM RPC API adapters. pub mod rpc_interface; /// Seccomp filter utilities. -pub mod seccomp_filters; +pub mod seccomp; /// Signal handling utilities. pub mod signal_handler; /// Serialization and deserialization facilities @@ -111,6 +111,9 @@ pub mod vmm_config; /// Module with virtual state structs. pub mod vstate; +/// Module with initrd. +pub mod initrd; + use std::collections::HashMap; use std::io; use std::os::unix::io::AsRawFd; @@ -122,12 +125,13 @@ use device_manager::acpi::ACPIDeviceManager; use device_manager::resources::ResourceAllocator; use devices::acpi::vmgenid::VmGenIdError; use event_manager::{EventManager as BaseEventManager, EventOps, Events, MutEventSubscriber}; -use seccompiler::BpfProgram; +use seccomp::BpfProgram; use userfaultfd::Uffd; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::terminal::Terminal; -use vstate::vcpu::{self, KvmVcpuConfigureError, StartThreadedError, VcpuSendEventError}; +use vstate::kvm::Kvm; +use vstate::vcpu::{self, StartThreadedError, VcpuSendEventError}; use crate::arch::DeviceType; use crate::cpu_config::templates::CpuConfiguration; @@ -136,20 +140,17 @@ use crate::device_manager::legacy::PortIODeviceManager; use crate::device_manager::mmio::MMIODeviceManager; use crate::devices::legacy::{IER_RDA_BIT, IER_RDA_OFFSET}; use crate::devices::virtio::balloon::{ - Balloon, BalloonConfig, BalloonError, BalloonStats, BALLOON_DEV_ID, + BALLOON_DEV_ID, Balloon, BalloonConfig, BalloonError, BalloonStats, }; use crate::devices::virtio::block::device::Block; use crate::devices::virtio::net::Net; use crate::devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_NET}; -use crate::logger::{error, info, warn, MetricsError, METRICS}; +use crate::logger::{METRICS, MetricsError, error, info, warn}; use crate::persist::{MicrovmState, MicrovmStateError, VmInfo}; use crate::rate_limiter::BucketUpdate; use crate::snapshot::Persist; -use crate::utils::u64_to_usize; use crate::vmm_config::instance_info::{InstanceInfo, VmState}; -use crate::vstate::memory::{ - GuestMemory, GuestMemoryExtension, GuestMemoryMmap, GuestMemoryRegion, -}; +use crate::vstate::memory::{GuestMemory, GuestMemoryMmap, GuestMemoryRegion}; use crate::vstate::vcpu::VcpuState; pub use crate::vstate::vcpu::{Vcpu, VcpuConfig, VcpuEvent, VcpuHandle, VcpuResponse}; pub use crate::vstate::vm::Vm; @@ -204,6 +205,8 @@ pub const HTTP_MAX_PAYLOAD_SIZE: usize = 51200; /// have permissions to open the KVM fd). #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum VmmError { + /// Failed to allocate guest resource: {0} + AllocateResources(#[from] vm_allocator::Error), #[cfg(target_arch = "aarch64")] /// Invalid command line error. Cmdline, @@ -215,8 +218,6 @@ pub enum VmmError { EventFd(io::Error), /// I8042 error: {0} I8042Error(devices::legacy::I8042DeviceError), - /// Cannot access kernel file: {0} - KernelFile(io::Error), #[cfg(target_arch = "x86_64")] /// Cannot add devices to the legacy I/O Bus. {0} LegacyIOBus(device_manager::legacy::LegacyDeviceError), @@ -225,22 +226,17 @@ pub enum VmmError { /// Cannot add a device to the MMIO Bus. {0} RegisterMMIODevice(device_manager::mmio::MmioError), /// Cannot install seccomp filters: {0} - SeccompFilters(seccompiler::InstallationError), + SeccompFilters(seccomp::InstallationError), /// Error writing to the serial console: {0} Serial(io::Error), /// Error creating timer fd: {0} TimerFd(io::Error), - /// Error configuring the vcpu for boot: {0} - VcpuConfigure(KvmVcpuConfigureError), /// Error creating the vcpu: {0} VcpuCreate(vstate::vcpu::VcpuError), /// Cannot send event to vCPU. {0} VcpuEvent(vstate::vcpu::VcpuError), /// Cannot create a vCPU handle. {0} VcpuHandle(vstate::vcpu::VcpuError), - #[cfg(target_arch = "aarch64")] - /// Error initializing the vcpu: {0} - VcpuInit(vstate::vcpu::KvmVcpuError), /// Failed to start vCPUs VcpuStart(StartVcpusError), /// Failed to pause the vCPUs. @@ -254,7 +250,9 @@ pub enum VmmError { /// Cannot spawn Vcpu thread: {0} VcpuSpawn(io::Error), /// Vm error: {0} - Vm(vstate::vm::VmError), + Vm(#[from] vstate::vm::VmError), + /// Kvm error: {0} + Kvm(#[from] vstate::kvm::KvmError), /// Error thrown by observer object on Vmm initialization: {0} VmmObserverInit(vmm_sys_util::errno::Error), /// Error thrown by observer object on Vmm teardown: {0} @@ -264,7 +262,7 @@ pub enum VmmError { } /// Shorthand type for KVM dirty page bitmap. -pub type DirtyBitmap = HashMap>; +pub type DirtyBitmap = HashMap>; /// Returns the size of guest memory, in MiB. pub(crate) fn mem_size_mib(guest_memory: &GuestMemoryMmap) -> u64 { @@ -307,11 +305,10 @@ pub struct Vmm { shutdown_exit_code: Option, // Guest VM core resources. - vm: Vm, - guest_memory: GuestMemoryMmap, + kvm: Kvm, + /// VM object + pub vm: Vm, // Save UFFD in order to keep it open in the Firecracker process, as well. - // Since this field is never read again, we need to allow `dead_code`. - #[allow(dead_code)] uffd: Option, vcpus_handles: Vec, // Used by Vcpus and devices to initiate teardown; Vmm should never write here. @@ -368,15 +365,13 @@ impl Vmm { if let Some(stdin) = self.events_observer.as_mut() { // Set raw mode for stdin. - stdin.lock().set_raw_mode().map_err(|err| { + stdin.lock().set_raw_mode().inspect_err(|&err| { warn!("Cannot set raw mode for the terminal. {:?}", err); - err })?; // Set non blocking stdin. - stdin.lock().set_non_block(true).map_err(|err| { + stdin.lock().set_non_block(true).inspect_err(|&err| { warn!("Cannot set non block for the terminal. {:?}", err); - err })?; } @@ -446,11 +441,6 @@ impl Vmm { Ok(()) } - /// Returns a reference to the inner `GuestMemoryMmap` object. - pub fn guest_memory(&self) -> &GuestMemoryMmap { - &self.guest_memory - } - /// Sets RDA bit in serial console pub fn emulate_serial_init(&self) -> Result<(), EmulateSerialInitError> { // When restoring from a previously saved state, there is no serial @@ -513,6 +503,7 @@ impl Vmm { pub fn save_state(&mut self, vm_info: &VmInfo) -> Result { use self::MicrovmStateError::SaveVmState; let vcpu_states = self.save_vcpu_states()?; + let kvm_state = self.kvm.save_state(); let vm_state = { #[cfg(target_arch = "x86_64")] { @@ -527,12 +518,11 @@ impl Vmm { }; let device_states = self.mmio_device_manager.save(); - let memory_state = self.guest_memory().describe(); let acpi_dev_state = self.acpi_device_manager.save(); Ok(MicrovmState { vm_info: vm_info.clone(), - memory_state, + kvm_state, vm_state, vcpu_states, device_states, @@ -596,49 +586,6 @@ impl Vmm { Ok(cpu_configs) } - /// Retrieves the KVM dirty bitmap for each of the guest's memory regions. - pub fn reset_dirty_bitmap(&self) { - self.guest_memory - .iter() - .enumerate() - .for_each(|(slot, region)| { - let _ = self - .vm - .fd() - .get_dirty_log(u32::try_from(slot).unwrap(), u64_to_usize(region.len())); - }); - } - - /// Retrieves the KVM dirty bitmap for each of the guest's memory regions. - pub fn get_dirty_bitmap(&self) -> Result { - let mut bitmap: DirtyBitmap = HashMap::new(); - self.guest_memory - .iter() - .enumerate() - .try_for_each(|(slot, region)| { - let bitmap_region = self - .vm - .fd() - .get_dirty_log(u32::try_from(slot).unwrap(), u64_to_usize(region.len()))?; - bitmap.insert(slot, bitmap_region); - Ok(()) - }) - .map_err(VmmError::DirtyBitmap)?; - Ok(bitmap) - } - - /// Enables or disables KVM dirty page tracking. - pub fn set_dirty_page_tracking(&mut self, enable: bool) -> Result<(), VmmError> { - // This function _always_ results in an ioctl update. The VMM is stateless in the sense - // that it's unaware of the current dirty page tracking setting. - // The VMM's consumer will need to cache the dirty tracking setting internally. For - // example, if this function were to be exposed through the VMM controller, the VMM - // resources should cache the flag. - self.vm - .set_kvm_memory_regions(&self.guest_memory, enable) - .map_err(VmmError::Vm) - } - /// Updates the path of the host file backing the emulated block device with id `drive_id`. /// We update the disk image on the device and its virtio configuration. pub fn update_block_device_path( @@ -753,7 +700,7 @@ impl Vmm { pub fn update_balloon_config(&mut self, amount_mib: u32) -> Result<(), BalloonError> { // The balloon cannot have a target size greater than the size of // the guest memory. - if u64::from(amount_mib) > mem_size_mib(self.guest_memory()) { + if u64::from(amount_mib) > mem_size_mib(self.vm.guest_memory()) { return Err(BalloonError::TooManyPagesRequested); } @@ -906,9 +853,8 @@ impl Drop for Vmm { self.stop(self.shutdown_exit_code.unwrap_or(FcExitCode::Ok)); if let Some(observer) = self.events_observer.as_mut() { - let res = observer.lock().set_canon_mode().map_err(|err| { + let res = observer.lock().set_canon_mode().inspect_err(|&err| { warn!("Cannot set canonical mode for the terminal. {:?}", err); - err }); if let Err(err) = res { warn!("{}", VmmError::VmmObserverTeardown(err)); diff --git a/src/vmm/src/logger/metrics.rs b/src/vmm/src/logger/metrics.rs index bcde569115e..e793495e1f1 100644 --- a/src/vmm/src/logger/metrics.rs +++ b/src/vmm/src/logger/metrics.rs @@ -46,10 +46,9 @@ //! //! The system implements 2 types of metrics: //! * Shared Incremental Metrics (SharedIncMetrics) - dedicated for the metrics which need a counter -//! (i.e the number of times an API request failed). These metrics are reset upon flush. +//! (i.e the number of times an API request failed). These metrics are reset upon flush. //! * Shared Store Metrics (SharedStoreMetrics) - are targeted at keeping a persistent value, it is -//! not -//! intended to act as a counter (i.e for measure the process start up time for example). +//! not intended to act as a counter (i.e for measure the process start up time for example). //! //! The current approach for the `SharedIncMetrics` type is to store two values (current and //! previous) and compute the delta between them each time we do a flush (i.e by serialization). @@ -58,6 +57,7 @@ //! to actual writing, so less synchronization effort is required. //! * We don't have to worry at all that much about losing some data if writing fails for a while //! (this could be a concern, I guess). +//! //! If if turns out this approach is not really what we want, it's pretty easy to resort to //! something else, while working behind the same interface. @@ -68,7 +68,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Mutex, OnceLock}; use serde::{Serialize, Serializer}; -use utils::time::{get_time_ns, get_time_us, ClockType}; +use utils::time::{ClockType, get_time_ns, get_time_us}; use super::FcLineWriter; use crate::devices::legacy; @@ -706,7 +706,7 @@ impl<'a> LatencyMetricsRecorder<'a> { } } } -impl<'a> Drop for LatencyMetricsRecorder<'a> { +impl Drop for LatencyMetricsRecorder<'_> { /// records aggregate (min/max/sum) for the given metric /// This captures delta between self.start_time and current time /// and updates min/max/sum metrics. @@ -951,8 +951,8 @@ impl FirecrackerMetrics { #[cfg(test)] mod tests { use std::io::{ErrorKind, LineWriter}; - use std::sync::atomic::fence; use std::sync::Arc; + use std::sync::atomic::fence; use std::thread; use vmm_sys_util::tempfile::TempFile; diff --git a/src/vmm/src/logger/mod.rs b/src/vmm/src/logger/mod.rs index 49d765b597b..4bbbf9e19c8 100644 --- a/src/vmm/src/logger/mod.rs +++ b/src/vmm/src/logger/mod.rs @@ -7,16 +7,16 @@ mod logging; mod metrics; -pub use log::{debug, error, info, log_enabled, trace, warn, Level}; +pub use log::{Level, debug, error, info, log_enabled, trace, warn}; pub use logging::{ - LevelFilter, LevelFilterFromStrError, LoggerConfig, LoggerInitError, LoggerUpdateError, - DEFAULT_INSTANCE_ID, DEFAULT_LEVEL, INSTANCE_ID, LOGGER, + DEFAULT_INSTANCE_ID, DEFAULT_LEVEL, INSTANCE_ID, LOGGER, LevelFilter, LevelFilterFromStrError, + LoggerConfig, LoggerInitError, LoggerUpdateError, }; pub use metrics::{ - IncMetric, LatencyAggregateMetrics, MetricsError, ProcessTimeReporter, SharedIncMetric, - SharedStoreMetric, StoreMetric, METRICS, + IncMetric, LatencyAggregateMetrics, METRICS, MetricsError, ProcessTimeReporter, + SharedIncMetric, SharedStoreMetric, StoreMetric, }; -use utils::time::{get_time_us, ClockType}; +use utils::time::{ClockType, get_time_us}; /// Alias for `std::io::LineWriter`. pub type FcLineWriter = std::io::LineWriter; diff --git a/src/vmm/src/mmds/data_store.rs b/src/vmm/src/mmds/data_store.rs index 1cbb2dfd088..518ae7d901a 100644 --- a/src/vmm/src/mmds/data_store.rs +++ b/src/vmm/src/mmds/data_store.rs @@ -5,7 +5,7 @@ use std::fmt; use std::fmt::{Display, Formatter}; use serde::{Deserialize, Serialize}; -use serde_json::{to_vec, Value}; +use serde_json::{Value, to_vec}; use crate::mmds::token::{MmdsTokenError as TokenError, TokenAuthority}; @@ -281,7 +281,7 @@ mod tests { use super::*; impl Mmds { - pub fn get_data_str(&self) -> String { + fn get_data_str(&self) -> String { if self.data_store.is_null() { return String::from("{}"); } diff --git a/src/vmm/src/mmds/mod.rs b/src/vmm/src/mmds/mod.rs index d4d64e58ffa..cdc0052eb42 100644 --- a/src/vmm/src/mmds/mod.rs +++ b/src/vmm/src/mmds/mod.rs @@ -142,7 +142,7 @@ fn respond_to_request_mmdsv2(mmds: &mut Mmds, request: Request) -> Response { request.http_version(), StatusCode::BadRequest, Body::new(err.to_string()), - ) + ); } }; diff --git a/src/vmm/src/mmds/ns.rs b/src/vmm/src/mmds/ns.rs index 09d73b21e99..8f9a764a7c1 100644 --- a/src/vmm/src/mmds/ns.rs +++ b/src/vmm/src/mmds/ns.rs @@ -13,19 +13,19 @@ use std::sync::{Arc, Mutex}; use utils::time::timestamp_cycles; +use crate::dumbo::pdu::Incomplete; use crate::dumbo::pdu::arp::{ - test_speculative_tpa, ArpError as ArpFrameError, EthIPv4ArpFrame, ETH_IPV4_FRAME_LEN, + ArpError as ArpFrameError, ETH_IPV4_FRAME_LEN, EthIPv4ArpFrame, test_speculative_tpa, }; use crate::dumbo::pdu::ethernet::{ - EthernetError as EthernetFrameError, EthernetFrame, ETHERTYPE_ARP, ETHERTYPE_IPV4, + ETHERTYPE_ARP, ETHERTYPE_IPV4, EthernetError as EthernetFrameError, EthernetFrame, }; use crate::dumbo::pdu::ipv4::{ - test_speculative_dst_addr, IPv4Packet, Ipv4Error as IPv4PacketError, PROTOCOL_TCP, + IPv4Packet, Ipv4Error as IPv4PacketError, PROTOCOL_TCP, test_speculative_dst_addr, }; use crate::dumbo::pdu::tcp::TcpError as TcpSegmentError; -use crate::dumbo::pdu::Incomplete; -use crate::dumbo::tcp::handler::{RecvEvent, TcpIPv4Handler, WriteEvent, WriteNextError}; use crate::dumbo::tcp::NextSegmentStatus; +use crate::dumbo::tcp::handler::{RecvEvent, TcpIPv4Handler, WriteEvent, WriteNextError}; use crate::logger::{IncMetric, METRICS}; use crate::mmds::data_store::Mmds; use crate::utils::net::mac::MacAddr; @@ -81,8 +81,6 @@ impl MmdsNetworkStack { mac_addr: MacAddr, ipv4_addr: Ipv4Addr, tcp_port: u16, - max_connections: NonZeroUsize, - max_pending_resets: NonZeroUsize, mmds: Arc>, ) -> Self { MmdsNetworkStack { @@ -93,8 +91,8 @@ impl MmdsNetworkStack { tcp_handler: TcpIPv4Handler::new( ipv4_addr, tcp_port, - max_connections, - max_pending_resets, + NonZeroUsize::new(DEFAULT_MAX_CONNECTIONS).unwrap(), + NonZeroUsize::new(DEFAULT_MAX_PENDING_RESETS).unwrap(), ), mmds, } @@ -105,14 +103,7 @@ impl MmdsNetworkStack { let ipv4_addr = mmds_ipv4_addr.unwrap_or_else(|| Ipv4Addr::from(DEFAULT_IPV4_ADDR)); // The unwrap()s are safe because the given literals are greater than 0. - Self::new( - mac_addr, - ipv4_addr, - DEFAULT_TCP_PORT, - NonZeroUsize::new(DEFAULT_MAX_CONNECTIONS).unwrap(), - NonZeroUsize::new(DEFAULT_MAX_PENDING_RESETS).unwrap(), - mmds, - ) + Self::new(mac_addr, ipv4_addr, DEFAULT_TCP_PORT, mmds) } pub fn set_ipv4_addr(&mut self, ipv4_addr: Ipv4Addr) { @@ -562,14 +553,8 @@ mod tests { let ip = Ipv4Addr::from(DEFAULT_IPV4_ADDR); let other_ip = Ipv4Addr::new(5, 6, 7, 8); let mac = MacAddr::from_bytes_unchecked(&[0; 6]); - let mut ns = MmdsNetworkStack::new( - mac, - ip, - DEFAULT_TCP_PORT, - NonZeroUsize::new(DEFAULT_MAX_CONNECTIONS).unwrap(), - NonZeroUsize::new(DEFAULT_MAX_PENDING_RESETS).unwrap(), - Arc::new(Mutex::new(Mmds::default())), - ); + let mut ns = + MmdsNetworkStack::new_with_defaults(Some(ip), Arc::new(Mutex::new(Mmds::default()))); let mut eth = EthernetFrame::write_incomplete(buf.as_mut(), mac, mac, ETHERTYPE_ARP).unwrap(); @@ -589,14 +574,8 @@ mod tests { let ip = Ipv4Addr::from(DEFAULT_IPV4_ADDR); let other_ip = Ipv4Addr::new(5, 6, 7, 8); let mac = MacAddr::from_bytes_unchecked(&[0; 6]); - let ns = MmdsNetworkStack::new( - mac, - ip, - DEFAULT_TCP_PORT, - NonZeroUsize::new(DEFAULT_MAX_CONNECTIONS).unwrap(), - NonZeroUsize::new(DEFAULT_MAX_PENDING_RESETS).unwrap(), - Arc::new(Mutex::new(Mmds::default())), - ); + let ns = + MmdsNetworkStack::new_with_defaults(Some(ip), Arc::new(Mutex::new(Mmds::default()))); let mut eth = EthernetFrame::write_incomplete(buf.as_mut(), mac, mac, ETHERTYPE_IPV4).unwrap(); @@ -615,14 +594,8 @@ mod tests { let ip = Ipv4Addr::from(DEFAULT_IPV4_ADDR); let other_ip = Ipv4Addr::new(5, 6, 7, 8); let mac = MacAddr::from_bytes_unchecked(&[0; 6]); - let mut ns = MmdsNetworkStack::new( - mac, - ip, - DEFAULT_TCP_PORT, - NonZeroUsize::new(DEFAULT_MAX_CONNECTIONS).unwrap(), - NonZeroUsize::new(DEFAULT_MAX_PENDING_RESETS).unwrap(), - Arc::new(Mutex::new(Mmds::default())), - ); + let mut ns = + MmdsNetworkStack::new_with_defaults(Some(ip), Arc::new(Mutex::new(Mmds::default()))); // try IPv4 with detour_arp let mut eth = diff --git a/src/vmm/src/mmds/persist.rs b/src/vmm/src/mmds/persist.rs index dc0113f8a5c..10b09c4f7ae 100644 --- a/src/vmm/src/mmds/persist.rs +++ b/src/vmm/src/mmds/persist.rs @@ -4,7 +4,6 @@ //! Defines the structures needed for saving/restoring MmdsNetworkStack. use std::net::Ipv4Addr; -use std::num::NonZeroUsize; use std::sync::{Arc, Mutex}; use serde::{Deserialize, Serialize}; @@ -12,7 +11,7 @@ use serde::{Deserialize, Serialize}; use super::ns::MmdsNetworkStack; use crate::mmds::data_store::Mmds; use crate::snapshot::Persist; -use crate::utils::net::mac::{MacAddr, MAC_ADDR_LEN}; +use crate::utils::net::mac::{MAC_ADDR_LEN, MacAddr}; /// State of a MmdsNetworkStack. #[derive(Debug, Clone, Serialize, Deserialize)] @@ -20,8 +19,6 @@ pub struct MmdsNetworkStackState { mac_addr: [u8; MAC_ADDR_LEN as usize], ipv4_addr: u32, tcp_port: u16, - max_connections: NonZeroUsize, - max_pending_resets: NonZeroUsize, } impl Persist<'_> for MmdsNetworkStack { @@ -37,8 +34,6 @@ impl Persist<'_> for MmdsNetworkStack { mac_addr, ipv4_addr: self.ipv4_addr.into(), tcp_port: self.tcp_handler.local_port(), - max_connections: self.tcp_handler.max_connections(), - max_pending_resets: self.tcp_handler.max_pending_resets(), } } @@ -50,8 +45,6 @@ impl Persist<'_> for MmdsNetworkStack { MacAddr::from_bytes_unchecked(&state.mac_addr), Ipv4Addr::from(state.ipv4_addr), state.tcp_port, - state.max_connections, - state.max_pending_resets, mmds, )) } @@ -83,13 +76,5 @@ mod tests { restored_ns.tcp_handler.local_port(), ns.tcp_handler.local_port() ); - assert_eq!( - restored_ns.tcp_handler.max_connections(), - ns.tcp_handler.max_connections() - ); - assert_eq!( - restored_ns.tcp_handler.max_pending_resets(), - ns.tcp_handler.max_pending_resets() - ); } } diff --git a/src/vmm/src/mmds/token.rs b/src/vmm/src/mmds/token.rs index c1d211141cc..e902303593d 100644 --- a/src/vmm/src/mmds/token.rs +++ b/src/vmm/src/mmds/token.rs @@ -10,9 +10,10 @@ use std::{fmt, io}; use aes_gcm::{AeadInPlace, Aes256Gcm, Key, KeyInit, Nonce}; use base64::Engine; -use bincode::{DefaultOptions, Error as BincodeError, Options}; +use bincode::config; +use bincode::config::{Configuration, Fixint, Limit, LittleEndian}; use serde::{Deserialize, Serialize}; -use utils::time::{get_time_ms, ClockType}; +use utils::time::{ClockType, get_time_ms}; /// Length of initialization vector. pub const IV_LEN: usize = 12; @@ -45,6 +46,12 @@ const TOKEN_LENGTH_LIMIT: usize = 70; /// too much memory when deserializing tokens. const DESERIALIZATION_BYTES_LIMIT: usize = std::mem::size_of::(); +const BINCODE_CONFIG: Configuration> = + config::standard() + .with_fixed_int_encoding() + .with_limit::() + .with_little_endian(); + #[rustfmt::skip] #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum MmdsTokenError { @@ -57,7 +64,7 @@ pub enum MmdsTokenError { /// Invalid time to live value provided for token: {0}. Please provide a value between {MIN_TOKEN_TTL_SECONDS:} and {MAX_TOKEN_TTL_SECONDS:}. InvalidTtlValue(u32), /// Bincode serialization failed: {0}. - Serialization(#[from] BincodeError), + Serialization(#[from] bincode::error::EncodeError), /// Failed to encrypt token. TokenEncryption, } @@ -287,7 +294,7 @@ impl Token { /// Encode token structure into a string using base64 encoding. fn base64_encode(&self) -> Result { - let token_bytes: Vec = bincode::serialize(self)?; + let token_bytes: Vec = bincode::serde::encode_to_vec(self, BINCODE_CONFIG)?; // Encode token structure bytes into base64. Ok(base64::engine::general_purpose::STANDARD.encode(token_bytes)) @@ -299,12 +306,9 @@ impl Token { .decode(encoded_token) .map_err(|_| MmdsTokenError::ExpiryExtraction)?; - let token: Token = DefaultOptions::new() - .with_fixint_encoding() - .allow_trailing_bytes() - .with_limit(DESERIALIZATION_BYTES_LIMIT as u64) - .deserialize(&token_bytes) - .map_err(|_| MmdsTokenError::ExpiryExtraction)?; + let token: Token = bincode::serde::decode_from_slice(&token_bytes, BINCODE_CONFIG) + .map_err(|_| MmdsTokenError::ExpiryExtraction)? + .0; Ok(token) } } @@ -518,45 +522,4 @@ mod tests { assert!(!token_authority.is_valid(&token0)); assert!(!token_authority.is_valid(&token1)); } - - #[test] - fn test_error_display() { - assert_eq!( - MmdsTokenError::EntropyPool(io::Error::from_raw_os_error(0)).to_string(), - format!( - "Failed to extract entropy from /dev/urandom entropy pool: {}.", - io::Error::from_raw_os_error(0) - ) - ); - - assert_eq!( - MmdsTokenError::ExpiryExtraction.to_string(), - "Failed to extract expiry value from token." - ); - - assert_eq!( - MmdsTokenError::InvalidState.to_string(), - "Invalid token authority state." - ); - - assert_eq!( - MmdsTokenError::InvalidTtlValue(0).to_string(), - format!( - "Invalid time to live value provided for token: 0. Please provide a value between \ - {} and {}.", - MIN_TOKEN_TTL_SECONDS, MAX_TOKEN_TTL_SECONDS - ) - ); - - assert_eq!( - MmdsTokenError::Serialization(BincodeError::new(bincode::ErrorKind::SizeLimit)) - .to_string(), - "Bincode serialization failed: the size limit has been reached." - ); - - assert_eq!( - MmdsTokenError::TokenEncryption.to_string(), - "Failed to encrypt token." - ); - } } diff --git a/src/vmm/src/persist.rs b/src/vmm/src/persist.rs index 560d9d7c6b7..be2fbc0a040 100644 --- a/src/vmm/src/persist.rs +++ b/src/vmm/src/persist.rs @@ -6,42 +6,41 @@ use std::fmt::Debug; use std::fs::{File, OpenOptions}; use std::io::{self, Write}; +use std::mem::forget; use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixStream; use std::path::Path; use std::sync::{Arc, Mutex}; -use seccompiler::BpfThreadMap; use semver::Version; use serde::{Deserialize, Serialize}; use userfaultfd::{FeatureFlags, Uffd, UffdBuilder}; use vmm_sys_util::sock_ctrl_msg::ScmSocket; #[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::vcpu::{get_manufacturer_id_from_host, get_manufacturer_id_from_state}; +use crate::arch::aarch64::vcpu::get_manufacturer_id_from_host; use crate::builder::{self, BuildMicrovmFromSnapshotError}; use crate::cpu_config::templates::StaticCpuTemplate; #[cfg(target_arch = "x86_64")] -use crate::cpu_config::x86_64::cpuid::common::get_vendor_id_from_host; -#[cfg(target_arch = "x86_64")] use crate::cpu_config::x86_64::cpuid::CpuidTrait; +#[cfg(target_arch = "x86_64")] +use crate::cpu_config::x86_64::cpuid::common::get_vendor_id_from_host; use crate::device_manager::persist::{ACPIDeviceManagerState, DevicePersistError, DeviceStates}; use crate::logger::{info, warn}; use crate::resources::VmResources; +use crate::seccomp::BpfThreadMap; use crate::snapshot::Snapshot; use crate::utils::u64_to_usize; use crate::vmm_config::boot_source::BootSourceConfig; use crate::vmm_config::instance_info::InstanceInfo; -use crate::vmm_config::machine_config::{HugePageConfig, MachineConfigUpdate, VmConfigError}; -use crate::vmm_config::snapshot::{ - CreateSnapshotParams, LoadSnapshotParams, MemBackendType, SnapshotType, -}; -use crate::vstate::memory::{ - GuestMemory, GuestMemoryExtension, GuestMemoryMmap, GuestMemoryState, MemoryError, -}; +use crate::vmm_config::machine_config::{HugePageConfig, MachineConfigError, MachineConfigUpdate}; +use crate::vmm_config::snapshot::{CreateSnapshotParams, LoadSnapshotParams, MemBackendType}; +use crate::vstate::kvm::KvmState; +use crate::vstate::memory; +use crate::vstate::memory::{GuestMemoryState, GuestRegionMmap, MemoryError}; use crate::vstate::vcpu::{VcpuSendEventError, VcpuState}; use crate::vstate::vm::VmState; -use crate::{mem_size_mib, vstate, EventManager, Vmm, VmmError}; +use crate::{EventManager, Vmm, vstate}; /// Holds information related to the VM that is not part of VmState. #[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)] @@ -61,11 +60,11 @@ pub struct VmInfo { impl From<&VmResources> for VmInfo { fn from(value: &VmResources) -> Self { Self { - mem_size_mib: value.vm_config.mem_size_mib as u64, - smt: value.vm_config.smt, - cpu_template: StaticCpuTemplate::from(&value.vm_config.cpu_template), - boot_source: value.boot_source_config().clone(), - huge_pages: value.vm_config.huge_pages, + mem_size_mib: value.machine_config.mem_size_mib as u64, + smt: value.machine_config.smt, + cpu_template: StaticCpuTemplate::from(&value.machine_config.cpu_template), + boot_source: value.boot_source.config.clone(), + huge_pages: value.machine_config.huge_pages, } } } @@ -75,8 +74,8 @@ impl From<&VmResources> for VmInfo { pub struct MicrovmState { /// Miscellaneous VM info. pub vm_info: VmInfo, - /// Memory state. - pub memory_state: GuestMemoryState, + /// KVM KVM state. + pub kvm_state: KvmState, /// VM KVM state. pub vm_state: VmState, /// Vcpu states. @@ -105,28 +104,25 @@ pub struct GuestRegionUffdMapping { /// Offset in the backend file/buffer where the region contents are. pub offset: u64, /// The configured page size for this memory region. + pub page_size: usize, + /// The configured page size **in bytes** for this memory region. The name is + /// wrong but cannot be changed due to being API, so this field is deprecated, + /// to be removed in 2.0. + #[deprecated] pub page_size_kib: usize, } /// Errors related to saving and restoring Microvm state. #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum MicrovmStateError { - /// Compatibility checks failed: {0} - IncompatibleState(String), - /// Provided MicroVM state is invalid. - InvalidInput, /// Operation not allowed: {0} NotAllowed(String), /// Cannot restore devices: {0} RestoreDevices(DevicePersistError), - /// Cannot restore Vcpu state: {0} - RestoreVcpuState(vstate::vcpu::VcpuError), - /// Cannot restore Vm state: {0} - RestoreVmState(vstate::vm::VmError), /// Cannot save Vcpu state: {0} SaveVcpuState(vstate::vcpu::VcpuError), /// Cannot save Vm state: {0} - SaveVmState(vstate::vm::VmError), + SaveVmState(vstate::vm::ArchVmError), /// Cannot signal Vcpu: {0} SignalVcpu(VcpuSendEventError), /// Vcpu is in unexpected state. @@ -138,26 +134,21 @@ pub enum MicrovmStateError { #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum CreateSnapshotError { /// Cannot get dirty bitmap: {0} - DirtyBitmap(VmmError), - #[rustfmt::skip] - /// Cannot translate microVM version to snapshot data version - UnsupportedVersion, + DirtyBitmap(#[from] vmm_sys_util::errno::Error), /// Cannot write memory file: {0} - Memory(MemoryError), + Memory(#[from] MemoryError), /// Cannot perform {0} on the memory backing file: {1} MemoryBackingFile(&'static str, io::Error), /// Cannot save the microVM state: {0} MicrovmState(MicrovmStateError), /// Cannot serialize the microVM state: {0} - SerializeMicrovmState(crate::snapshot::SnapshotError), + SerializeMicrovmState(#[from] crate::snapshot::SnapshotError), /// Cannot perform {0} on the snapshot backing file: {1} SnapshotBackingFile(&'static str, io::Error), - /// Size mismatch when writing diff snapshot on top of base layer: base layer size is {0} but diff layer is size {1}. - SnapshotBackingFileLengthMismatch(u64, u64), } /// Snapshot version -pub const SNAPSHOT_VERSION: Version = Version::new(3, 0, 0); +pub const SNAPSHOT_VERSION: Version = Version::new(5, 0, 0); /// Creates a Microvm snapshot. pub fn create_snapshot( @@ -171,7 +162,25 @@ pub fn create_snapshot( snapshot_state_to_file(µvm_state, ¶ms.snapshot_path)?; - snapshot_memory_to_file(vmm, ¶ms.mem_file_path, params.snapshot_type)?; + vmm.vm + .snapshot_memory_to_file(¶ms.mem_file_path, params.snapshot_type)?; + + // We need to mark queues as dirty again for all activated devices. The reason we + // do it here is because we don't mark pages as dirty during runtime + // for queue objects. + // SAFETY: + // This should never fail as we only mark pages only if device has already been activated, + // and the address validation was already performed on device activation. + vmm.mmio_device_manager + .for_each_virtio_device(|_, _, _, dev| { + let d = dev.lock().unwrap(); + if d.is_activated() { + d.mark_queue_memory_dirty(vmm.vm.guest_memory()) + } else { + Ok(()) + } + }) + .unwrap(); Ok(()) } @@ -189,9 +198,7 @@ fn snapshot_state_to_file( .map_err(|err| SnapshotBackingFile("open", err))?; let snapshot = Snapshot::new(SNAPSHOT_VERSION); - snapshot - .save(&mut snapshot_file, microvm_state) - .map_err(SerializeMicrovmState)?; + snapshot.save(&mut snapshot_file, microvm_state)?; snapshot_file .flush() .map_err(|err| SnapshotBackingFile("flush", err))?; @@ -200,96 +207,6 @@ fn snapshot_state_to_file( .map_err(|err| SnapshotBackingFile("sync_all", err)) } -/// Takes a snapshot of the virtual machine running inside the given [`Vmm`] and saves it to -/// `mem_file_path`. -/// -/// If `snapshot_type` is [`SnapshotType::Diff`], and `mem_file_path` exists and is a snapshot file -/// of matching size, then the diff snapshot will be directly merged into the existing snapshot. -/// Otherwise, existing files are simply overwritten. -fn snapshot_memory_to_file( - vmm: &Vmm, - mem_file_path: &Path, - snapshot_type: SnapshotType, -) -> Result<(), CreateSnapshotError> { - use self::CreateSnapshotError::*; - - // Need to check this here, as we create the file in the line below - let file_existed = mem_file_path.exists(); - - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(false) - .open(mem_file_path) - .map_err(|err| MemoryBackingFile("open", err))?; - - // Determine what size our total memory area is. - let mem_size_mib = mem_size_mib(vmm.guest_memory()); - let expected_size = mem_size_mib * 1024 * 1024; - - if file_existed { - let file_size = file - .metadata() - .map_err(|e| MemoryBackingFile("get_metadata", e))? - .len(); - - // Here we only truncate the file if the size mismatches. - // - For full snapshots, the entire file's contents will be overwritten anyway. We have to - // avoid truncating here to deal with the edge case where it represents the snapshot file - // from which this very microVM was loaded (as modifying the memory file would be - // reflected in the mmap of the file, meaning a truncate operation would zero out guest - // memory, and thus corrupt the VM). - // - For diff snapshots, we want to merge the diff layer directly into the file. - if file_size != expected_size { - file.set_len(0) - .map_err(|err| MemoryBackingFile("truncate", err))?; - } - } - - // Set the length of the file to the full size of the memory area. - file.set_len(expected_size) - .map_err(|e| MemoryBackingFile("set_length", e))?; - - match snapshot_type { - SnapshotType::Diff => { - let dirty_bitmap = vmm.get_dirty_bitmap().map_err(DirtyBitmap)?; - vmm.guest_memory() - .dump_dirty(&mut file, &dirty_bitmap) - .map_err(Memory) - } - SnapshotType::Full => { - let dump_res = vmm.guest_memory().dump(&mut file).map_err(Memory); - if dump_res.is_ok() { - vmm.reset_dirty_bitmap(); - vmm.guest_memory().reset_dirty(); - } - - dump_res - } - }?; - // We need to mark queues as dirty again for all activated devices. The reason we - // do it here is because we don't mark pages as dirty during runtime - // for queue objects. - // SAFETY: - // This should never fail as we only mark pages only if device has already been activated, - // and the address validation was already performed on device activation. - vmm.mmio_device_manager - .for_each_virtio_device(|_, _, _, dev| { - let d = dev.lock().unwrap(); - if d.is_activated() { - d.mark_queue_memory_dirty(vmm.guest_memory()) - } else { - Ok(()) - } - }) - .unwrap(); - - file.flush() - .map_err(|err| MemoryBackingFile("flush", err))?; - file.sync_all() - .map_err(|err| MemoryBackingFile("sync_all", err)) -} - /// Validates that snapshot CPU vendor matches the host CPU vendor. /// /// # Errors @@ -336,24 +253,24 @@ pub fn validate_cpu_vendor(microvm_state: &MicrovmState) { #[cfg(target_arch = "aarch64")] pub fn validate_cpu_manufacturer_id(microvm_state: &MicrovmState) { let host_cpu_id = get_manufacturer_id_from_host(); - let snapshot_cpu_id = get_manufacturer_id_from_state(µvm_state.vcpu_states[0].regs); + let snapshot_cpu_id = microvm_state.vcpu_states[0].regs.manifacturer_id(); match (host_cpu_id, snapshot_cpu_id) { - (Ok(host_id), Ok(snapshot_id)) => { + (Ok(host_id), Some(snapshot_id)) => { info!("Host CPU manufacturer ID: {host_id:?}"); info!("Snapshot CPU manufacturer ID: {snapshot_id:?}"); if host_id != snapshot_id { warn!("Host CPU manufacturer ID differs from the snapshotted one",); } } - (Ok(host_id), Err(_)) => { + (Ok(host_id), None) => { info!("Host CPU manufacturer ID: {host_id:?}"); warn!("Snapshot CPU manufacturer ID: couldn't get from the snapshot"); } - (Err(_), Ok(snapshot_id)) => { + (Err(_), Some(snapshot_id)) => { warn!("Host CPU manufacturer ID: couldn't get from the host"); info!("Snapshot CPU manufacturer ID: {snapshot_id:?}"); } - (Err(_), Err(_)) => { + (Err(_), None) => { warn!("Host CPU manufacturer ID: couldn't get from the host"); warn!("Snapshot CPU manufacturer ID: couldn't get from the snapshot"); } @@ -373,7 +290,7 @@ pub fn snapshot_state_sanity_check( // Check if the snapshot contains at least 1 mem region. // Upper bound check will be done when creating guest memory by comparing against // KVM max supported value kvm_context.max_memslots(). - if microvm_state.memory_state.regions.is_empty() { + if microvm_state.vm_state.memory.regions.is_empty() { return Err(SnapShotStateSanityCheckError::NoMemory); } @@ -415,18 +332,32 @@ pub fn restore_from_snapshot( params: &LoadSnapshotParams, vm_resources: &mut VmResources, ) -> Result>, RestoreFromSnapshotError> { - let microvm_state = snapshot_state_from_file(¶ms.snapshot_path)?; + let mut microvm_state = snapshot_state_from_file(¶ms.snapshot_path)?; + for entry in ¶ms.network_overrides { + let net_devices = &mut microvm_state.device_states.net_devices; + if let Some(device) = net_devices + .iter_mut() + .find(|x| x.device_state.id == entry.iface_id) + { + device + .device_state + .tap_if_name + .clone_from(&entry.host_dev_name); + } else { + return Err(SnapshotStateFromFileError::UnknownNetworkDevice.into()); + } + } let track_dirty_pages = params.enable_diff_snapshots; let vcpu_count = microvm_state .vcpu_states .len() .try_into() - .map_err(|_| VmConfigError::InvalidVcpuCount) + .map_err(|_| MachineConfigError::InvalidVcpuCount) .map_err(BuildMicrovmFromSnapshotError::VmUpdateConfig)?; vm_resources - .update_vm_config(&MachineConfigUpdate { + .update_machine_config(&MachineConfigUpdate { vcpu_count: Some(vcpu_count), mem_size_mib: Some(u64_to_usize(microvm_state.vm_info.mem_size_mib)), smt: Some(microvm_state.vm_info.smt), @@ -442,27 +373,27 @@ pub fn restore_from_snapshot( snapshot_state_sanity_check(µvm_state)?; let mem_backend_path = ¶ms.mem_backend.backend_path; - let mem_state = µvm_state.memory_state; + let mem_state = µvm_state.vm_state.memory; let (guest_memory, uffd) = match params.mem_backend.backend_type { - MemBackendType::File => ( - guest_memory_from_file( - mem_backend_path, - mem_state, - track_dirty_pages, - vm_resources.vm_config.huge_pages, + MemBackendType::File => { + if vm_resources.machine_config.huge_pages.is_hugetlbfs() { + return Err(RestoreFromSnapshotGuestMemoryError::File( + GuestMemoryFromFileError::HugetlbfsSnapshot, + ) + .into()); + } + ( + guest_memory_from_file(mem_backend_path, mem_state, track_dirty_pages) + .map_err(RestoreFromSnapshotGuestMemoryError::File)?, + None, ) - .map_err(RestoreFromSnapshotGuestMemoryError::File)?, - None, - ), + } MemBackendType::Uffd => guest_memory_from_uffd( mem_backend_path, mem_state, track_dirty_pages, - // We enable the UFFD_FEATURE_EVENT_REMOVE feature only if a balloon device - // is present in the microVM state. - microvm_state.device_states.balloon_device.is_some(), - vm_resources.vm_config.huge_pages, + vm_resources.machine_config.huge_pages, ) .map_err(RestoreFromSnapshotGuestMemoryError::Uffd)?, }; @@ -487,6 +418,8 @@ pub enum SnapshotStateFromFileError { Meta(std::io::Error), /// Failed to load snapshot state from file: {0} Load(#[from] crate::snapshot::SnapshotError), + /// Unknown Network Device. + UnknownNetworkDevice, } fn snapshot_state_from_file( @@ -510,17 +443,17 @@ pub enum GuestMemoryFromFileError { File(#[from] std::io::Error), /// Failed to restore guest memory: {0} Restore(#[from] MemoryError), + /// Cannot restore hugetlbfs backed snapshot by mapping the memory file. Please use uffd. + HugetlbfsSnapshot, } fn guest_memory_from_file( mem_file_path: &Path, mem_state: &GuestMemoryState, track_dirty_pages: bool, - huge_pages: HugePageConfig, -) -> Result { +) -> Result, GuestMemoryFromFileError> { let mem_file = File::open(mem_file_path)?; - let guest_mem = - GuestMemoryMmap::from_state(Some(&mem_file), mem_state, track_dirty_pages, huge_pages)?; + let guest_mem = memory::snapshot_file(mem_file, mem_state.regions(), track_dirty_pages)?; Ok(guest_mem) } @@ -543,19 +476,18 @@ fn guest_memory_from_uffd( mem_uds_path: &Path, mem_state: &GuestMemoryState, track_dirty_pages: bool, - enable_balloon: bool, huge_pages: HugePageConfig, -) -> Result<(GuestMemoryMmap, Option), GuestMemoryFromUffdError> { +) -> Result<(Vec, Option), GuestMemoryFromUffdError> { let (guest_memory, backend_mappings) = create_guest_memory(mem_state, track_dirty_pages, huge_pages)?; let mut uffd_builder = UffdBuilder::new(); - if enable_balloon { - // We enable this so that the page fault handler can add logic - // for treating madvise(MADV_DONTNEED) events triggerd by balloon inflation. - uffd_builder.require_features(FeatureFlags::EVENT_REMOVE); - } + // We only make use of this if balloon devices are present, but we can enable it unconditionally + // because the only place the kernel checks this is in a hook from madvise, e.g. it doesn't + // actively change the behavior of UFFD, only passively. Without balloon devices + // we never call madvise anyway, so no need to put this into a conditional. + uffd_builder.require_features(FeatureFlags::EVENT_REMOVE); let uffd = uffd_builder .close_on_exec(true) @@ -578,16 +510,20 @@ fn create_guest_memory( mem_state: &GuestMemoryState, track_dirty_pages: bool, huge_pages: HugePageConfig, -) -> Result<(GuestMemoryMmap, Vec), GuestMemoryFromUffdError> { - let guest_memory = GuestMemoryMmap::from_state(None, mem_state, track_dirty_pages, huge_pages)?; - let mut backend_mappings = Vec::with_capacity(guest_memory.num_regions()); - for (mem_region, state_region) in guest_memory.iter().zip(mem_state.regions.iter()) { +) -> Result<(Vec, Vec), GuestMemoryFromUffdError> { + let guest_memory = memory::anonymous(mem_state.regions(), track_dirty_pages, huge_pages)?; + let mut backend_mappings = Vec::with_capacity(guest_memory.len()); + let mut offset = 0; + for mem_region in guest_memory.iter() { + #[allow(deprecated)] backend_mappings.push(GuestRegionUffdMapping { base_host_virt_addr: mem_region.as_ptr() as u64, size: mem_region.size(), - offset: state_region.offset, - page_size_kib: huge_pages.page_size_kib(), + offset, + page_size: huge_pages.page_size(), + page_size_kib: huge_pages.page_size(), }); + offset += mem_region.size() as u64; } Ok((guest_memory, backend_mappings)) @@ -638,6 +574,11 @@ fn send_uffd_handshake( uffd.as_raw_fd(), )?; + // We prevent Rust from closing the socket file descriptor to avoid a potential race condition + // between the mappings message and the connection shutdown. If the latter arrives at the UFFD + // handler first, the handler never sees the mappings. + forget(socket); + Ok(()) } @@ -648,11 +589,12 @@ mod tests { use vmm_sys_util::tempfile::TempFile; use super::*; + use crate::Vmm; #[cfg(target_arch = "x86_64")] use crate::builder::tests::insert_vmgenid_device; use crate::builder::tests::{ - default_kernel_cmdline, default_vmm, insert_balloon_device, insert_block_devices, - insert_net_device, insert_vsock_device, CustomBlockConfig, + CustomBlockConfig, default_kernel_cmdline, default_vmm, insert_balloon_device, + insert_block_devices, insert_net_device, insert_vsock_device, }; #[cfg(target_arch = "aarch64")] use crate::construct_kvm_mpidrs; @@ -662,7 +604,6 @@ mod tests { use crate::vmm_config::net::NetworkInterfaceConfig; use crate::vmm_config::vsock::tests::default_config; use crate::vstate::memory::GuestMemoryRegionState; - use crate::Vmm; fn default_vmm_with_devices() -> Vmm { let mut event_manager = EventManager::new().expect("Cannot create EventManager"); @@ -728,14 +669,13 @@ mod tests { assert!(states.vsock_device.is_some()); assert!(states.balloon_device.is_some()); - let memory_state = vmm.guest_memory().describe(); let vcpu_states = vec![VcpuState::default()]; #[cfg(target_arch = "aarch64")] let mpidrs = construct_kvm_mpidrs(&vcpu_states); let microvm_state = MicrovmState { device_states: states, - memory_state, vcpu_states, + kvm_state: Default::default(), vm_info: VmInfo { mem_size_mib: 1u64, ..Default::default() @@ -766,7 +706,6 @@ mod tests { regions: vec![GuestMemoryRegionState { base_address: 0, size: 0x20000, - offset: 0x10000, }], }; @@ -775,27 +714,27 @@ mod tests { assert_eq!(uffd_regions.len(), 1); assert_eq!(uffd_regions[0].size, 0x20000); - assert_eq!(uffd_regions[0].offset, 0x10000); - assert_eq!( - uffd_regions[0].page_size_kib, - HugePageConfig::None.page_size_kib() - ); + assert_eq!(uffd_regions[0].offset, 0); + assert_eq!(uffd_regions[0].page_size, HugePageConfig::None.page_size()); } #[test] fn test_send_uffd_handshake() { + #[allow(deprecated)] let uffd_regions = vec![ GuestRegionUffdMapping { base_host_virt_addr: 0, size: 0x100000, offset: 0, - page_size_kib: HugePageConfig::None.page_size_kib(), + page_size: HugePageConfig::None.page_size(), + page_size_kib: HugePageConfig::None.page_size(), }, GuestRegionUffdMapping { base_host_virt_addr: 0x100000, size: 0x200000, offset: 0, - page_size_kib: HugePageConfig::Hugetlbfs2M.page_size_kib(), + page_size: HugePageConfig::Hugetlbfs2M.page_size(), + page_size_kib: HugePageConfig::Hugetlbfs2M.page_size(), }, ]; diff --git a/src/vmm/src/rate_limiter/mod.rs b/src/vmm/src/rate_limiter/mod.rs index b4c2a6d34ed..3ed5405bf01 100644 --- a/src/vmm/src/rate_limiter/mod.rs +++ b/src/vmm/src/rate_limiter/mod.rs @@ -328,15 +328,15 @@ impl RateLimiter { /// # Arguments /// /// * `bytes_total_capacity` - the total capacity of the `TokenType::Bytes` token bucket. - /// * `bytes_one_time_burst` - initial extra credit on top of `bytes_total_capacity`, - /// that does not replenish and which can be used for an initial burst of data. - /// * `bytes_complete_refill_time_ms` - number of milliseconds for the `TokenType::Bytes` - /// token bucket to go from zero Bytes to `bytes_total_capacity` Bytes. + /// * `bytes_one_time_burst` - initial extra credit on top of `bytes_total_capacity`, that does + /// not replenish and which can be used for an initial burst of data. + /// * `bytes_complete_refill_time_ms` - number of milliseconds for the `TokenType::Bytes` token + /// bucket to go from zero Bytes to `bytes_total_capacity` Bytes. /// * `ops_total_capacity` - the total capacity of the `TokenType::Ops` token bucket. - /// * `ops_one_time_burst` - initial extra credit on top of `ops_total_capacity`, - /// that does not replenish and which can be used for an initial burst of data. + /// * `ops_one_time_burst` - initial extra credit on top of `ops_total_capacity`, that does not + /// replenish and which can be used for an initial burst of data. /// * `ops_complete_refill_time_ms` - number of milliseconds for the `TokenType::Ops` token - /// bucket to go from zero Ops to `ops_total_capacity` Ops. + /// bucket to go from zero Ops to `ops_total_capacity` Ops. /// /// If either bytes/ops *size* or *refill_time* are **zero**, the limiter /// is **disabled** for that respective token type. @@ -794,7 +794,7 @@ pub(crate) mod tests { } // After a restore, we cannot be certain that the last_update field has the same value. - pub fn partial_eq(&self, other: &TokenBucket) -> bool { + pub(crate) fn partial_eq(&self, other: &TokenBucket) -> bool { (other.capacity() == self.capacity()) && (other.one_time_burst() == self.one_time_burst()) && (other.refill_time_ms() == self.refill_time_ms()) diff --git a/src/vmm/src/rate_limiter/persist.rs b/src/vmm/src/rate_limiter/persist.rs index 671e99ede38..fea5e9a8742 100644 --- a/src/vmm/src/rate_limiter/persist.rs +++ b/src/vmm/src/rate_limiter/persist.rs @@ -132,14 +132,18 @@ mod tests { let restored_rate_limiter = RateLimiter::restore((), &rate_limiter.save()).expect("Unable to restore rate limiter"); - assert!(rate_limiter - .ops() - .unwrap() - .partial_eq(restored_rate_limiter.ops().unwrap())); - assert!(rate_limiter - .bandwidth() - .unwrap() - .partial_eq(restored_rate_limiter.bandwidth().unwrap())); + assert!( + rate_limiter + .ops() + .unwrap() + .partial_eq(restored_rate_limiter.ops().unwrap()) + ); + assert!( + rate_limiter + .bandwidth() + .unwrap() + .partial_eq(restored_rate_limiter.bandwidth().unwrap()) + ); assert_eq!( restored_rate_limiter.timer_fd.get_state(), TimerState::Disarmed @@ -151,14 +155,18 @@ mod tests { let restored_rate_limiter = RateLimiter::restore((), &rate_limiter.save()).expect("Unable to restore rate limiter"); - assert!(rate_limiter - .ops() - .unwrap() - .partial_eq(restored_rate_limiter.ops().unwrap())); - assert!(rate_limiter - .bandwidth() - .unwrap() - .partial_eq(restored_rate_limiter.bandwidth().unwrap())); + assert!( + rate_limiter + .ops() + .unwrap() + .partial_eq(restored_rate_limiter.ops().unwrap()) + ); + assert!( + rate_limiter + .bandwidth() + .unwrap() + .partial_eq(restored_rate_limiter.bandwidth().unwrap()) + ); assert_eq!( restored_rate_limiter.timer_fd.get_state(), TimerState::Disarmed @@ -169,14 +177,18 @@ mod tests { let restored_rate_limiter = RateLimiter::restore((), &rate_limiter.save()).expect("Unable to restore rate limiter"); - assert!(rate_limiter - .ops() - .unwrap() - .partial_eq(restored_rate_limiter.ops().unwrap())); - assert!(rate_limiter - .bandwidth() - .unwrap() - .partial_eq(restored_rate_limiter.bandwidth().unwrap())); + assert!( + rate_limiter + .ops() + .unwrap() + .partial_eq(restored_rate_limiter.ops().unwrap()) + ); + assert!( + rate_limiter + .bandwidth() + .unwrap() + .partial_eq(restored_rate_limiter.bandwidth().unwrap()) + ); // Test serialization. let mut mem = vec![0; 4096]; @@ -184,13 +196,17 @@ mod tests { let restored_rate_limiter = RateLimiter::restore((), &Snapshot::deserialize(&mut mem.as_slice()).unwrap()).unwrap(); - assert!(rate_limiter - .ops() - .unwrap() - .partial_eq(restored_rate_limiter.ops().unwrap())); - assert!(rate_limiter - .bandwidth() - .unwrap() - .partial_eq(restored_rate_limiter.bandwidth().unwrap())); + assert!( + rate_limiter + .ops() + .unwrap() + .partial_eq(restored_rate_limiter.ops().unwrap()) + ); + assert!( + rate_limiter + .bandwidth() + .unwrap() + .partial_eq(restored_rate_limiter.bandwidth().unwrap()) + ); } } diff --git a/src/vmm/src/resources.rs b/src/vmm/src/resources.rs index a4d15641975..097e2041b55 100644 --- a/src/vmm/src/resources.rs +++ b/src/vmm/src/resources.rs @@ -9,10 +9,11 @@ use serde::{Deserialize, Serialize}; use crate::cpu_config::templates::CustomCpuTemplate; use crate::device_manager::persist::SharedDeviceType; -use crate::logger::{info, log_dev_preview_warning}; +use crate::logger::info; use crate::mmds; use crate::mmds::data_store::{Mmds, MmdsVersion}; use crate::mmds::ns::MmdsNetworkStack; +use crate::utils::mib_to_bytes; use crate::utils::net::ipv4addr::is_link_local_valid; use crate::vmm_config::balloon::*; use crate::vmm_config::boot_source::{ @@ -22,13 +23,14 @@ use crate::vmm_config::drive::*; use crate::vmm_config::entropy::*; use crate::vmm_config::instance_info::InstanceInfo; use crate::vmm_config::machine_config::{ - HugePageConfig, MachineConfig, MachineConfigUpdate, VmConfig, VmConfigError, + HugePageConfig, MachineConfig, MachineConfigError, MachineConfigUpdate, }; -use crate::vmm_config::metrics::{init_metrics, MetricsConfig, MetricsConfigError}; +use crate::vmm_config::metrics::{MetricsConfig, MetricsConfigError, init_metrics}; use crate::vmm_config::mmds::{MmdsConfig, MmdsConfigError}; use crate::vmm_config::net::*; use crate::vmm_config::vsock::*; -use crate::vstate::memory::{GuestMemoryExtension, GuestMemoryMmap, MemoryError}; +use crate::vstate::memory; +use crate::vstate::memory::{GuestRegionMmap, MemoryError}; /// Errors encountered when configuring microVM resources. #[derive(Debug, thiserror::Error, displaydoc::Display)] @@ -54,7 +56,7 @@ pub enum ResourcesError { /// Network device error: {0} NetDevice(#[from] NetworkInterfaceError), /// VM config error: {0} - VmConfig(#[from] VmConfigError), + MachineConfig(#[from] MachineConfigError), /// Vsock device error: {0} VsockDevice(#[from] VsockConfigError), /// Entropy device error: {0} @@ -63,29 +65,20 @@ pub enum ResourcesError { /// Used for configuring a vmm from one single json passed to the Firecracker process. #[derive(Debug, Default, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] pub struct VmmConfig { - #[serde(rename = "balloon")] - balloon_device: Option, - #[serde(rename = "drives")] - block_devices: Vec, - #[serde(rename = "boot-source")] + balloon: Option, + drives: Vec, boot_source: BootSourceConfig, - #[serde(rename = "cpu-config")] cpu_config: Option, - #[serde(rename = "logger")] logger: Option, - #[serde(rename = "machine-config")] machine_config: Option, - #[serde(rename = "metrics")] metrics: Option, - #[serde(rename = "mmds-config")] mmds_config: Option, - #[serde(rename = "network-interfaces", default)] - net_devices: Vec, - #[serde(rename = "vsock")] - vsock_device: Option, - #[serde(rename = "entropy")] - entropy_device: Option, + #[serde(default)] + network_interfaces: Vec, + vsock: Option, + entropy: Option, } /// A data structure that encapsulates the device configurations @@ -93,7 +86,7 @@ pub struct VmmConfig { #[derive(Debug, Default)] pub struct VmResources { /// The vCpu and memory configuration for this microVM. - pub vm_config: VmConfig, + pub machine_config: MachineConfig, /// The boot source spec (contains both config and builder) for this microVM. pub boot_source: BootSource, /// The block devices. @@ -140,7 +133,7 @@ impl VmResources { }; if let Some(machine_config) = vmm_config.machine_config { let machine_config = MachineConfigUpdate::from(machine_config); - resources.update_vm_config(&machine_config)?; + resources.update_machine_config(&machine_config)?; } if let Some(cpu_config) = vmm_config.cpu_config { @@ -152,19 +145,19 @@ impl VmResources { resources.build_boot_source(vmm_config.boot_source)?; - for drive_config in vmm_config.block_devices.into_iter() { + for drive_config in vmm_config.drives.into_iter() { resources.set_block_device(drive_config)?; } - for net_config in vmm_config.net_devices.into_iter() { + for net_config in vmm_config.network_interfaces.into_iter() { resources.build_net_device(net_config)?; } - if let Some(vsock_config) = vmm_config.vsock_device { + if let Some(vsock_config) = vmm_config.vsock { resources.set_vsock_device(vsock_config)?; } - if let Some(balloon_config) = vmm_config.balloon_device { + if let Some(balloon_config) = vmm_config.balloon { resources.set_balloon_device(balloon_config)?; } @@ -180,7 +173,7 @@ impl VmResources { resources.set_mmds_config(mmds_config, &instance_info.id)?; } - if let Some(entropy_device_config) = vmm_config.entropy_device { + if let Some(entropy_device_config) = vmm_config.entropy { resources.build_entropy_device(entropy_device_config)?; } @@ -219,7 +212,7 @@ impl VmResources { SharedDeviceType::Balloon(balloon) => { self.balloon.set_device(balloon); - if self.vm_config.huge_pages != HugePageConfig::None { + if self.machine_config.huge_pages != HugePageConfig::None { return Err(ResourcesError::BalloonDevice(BalloonConfigError::HugePages)); } } @@ -238,16 +231,15 @@ impl VmResources { /// Add a custom CPU template to the VM resources /// to configure vCPUs. pub fn set_custom_cpu_template(&mut self, cpu_template: CustomCpuTemplate) { - self.vm_config.set_custom_cpu_template(cpu_template); + self.machine_config.set_custom_cpu_template(cpu_template); } /// Updates the configuration of the microVM. - pub fn update_vm_config(&mut self, update: &MachineConfigUpdate) -> Result<(), VmConfigError> { - if update.huge_pages.is_some() && update.huge_pages != Some(HugePageConfig::None) { - log_dev_preview_warning("Huge pages support", None); - } - - let updated = self.vm_config.update(update)?; + pub fn update_machine_config( + &mut self, + update: &MachineConfigUpdate, + ) -> Result<(), MachineConfigError> { + let updated = self.machine_config.update(update)?; // The VM cannot have a memory size smaller than the target size // of the balloon device, if present. @@ -256,23 +248,16 @@ impl VmResources { < self .balloon .get_config() - .map_err(|_| VmConfigError::InvalidVmState)? + .map_err(|_| MachineConfigError::InvalidVmState)? .amount_mib as usize { - return Err(VmConfigError::IncompatibleBalloonSize); + return Err(MachineConfigError::IncompatibleBalloonSize); } if self.balloon.get().is_some() && updated.huge_pages != HugePageConfig::None { - return Err(VmConfigError::BalloonAndHugePages); + return Err(MachineConfigError::BalloonAndHugePages); } - - if self.boot_source.config.initrd_path.is_some() - && updated.huge_pages != HugePageConfig::None - { - return Err(VmConfigError::InitrdAndHugePages); - } - - self.vm_config = updated; + self.machine_config = updated; Ok(()) } @@ -315,16 +300,6 @@ impl VmResources { mmds_config } - /// Gets a reference to the boot source configuration. - pub fn boot_source_config(&self) -> &BootSourceConfig { - &self.boot_source.config - } - - /// Gets a reference to the boot source builder. - pub fn boot_source_builder(&self) -> Option<&BootConfig> { - self.boot_source.builder.as_ref() - } - /// Sets a balloon device to be attached when the VM starts. pub fn set_balloon_device( &mut self, @@ -332,11 +307,11 @@ impl VmResources { ) -> Result<(), BalloonConfigError> { // The balloon cannot have a target size greater than the size of // the guest memory. - if config.amount_mib as usize > self.vm_config.mem_size_mib { + if config.amount_mib as usize > self.machine_config.mem_size_mib { return Err(BalloonConfigError::TooManyPagesRequested); } - if self.vm_config.huge_pages != HugePageConfig::None { + if self.machine_config.huge_pages != HugePageConfig::None { return Err(BalloonConfigError::HugePages); } @@ -348,22 +323,14 @@ impl VmResources { &mut self, boot_source_cfg: BootSourceConfig, ) -> Result<(), BootSourceConfigError> { - if boot_source_cfg.initrd_path.is_some() - && self.vm_config.huge_pages != HugePageConfig::None - { - return Err(BootSourceConfigError::HugePagesAndInitRd); - } + self.boot_source = BootSource { + builder: Some(BootConfig::new(&boot_source_cfg)?), + config: boot_source_cfg, + }; - self.set_boot_source_config(boot_source_cfg); - self.boot_source.builder = Some(BootConfig::new(self.boot_source_config())?); Ok(()) } - /// Set the boot source configuration (contains raw kernel config details). - pub fn set_boot_source_config(&mut self, boot_source_cfg: BootSourceConfig) { - self.boot_source.config = boot_source_cfg; - } - /// Inserts a block to be attached when the VM starts. // Only call this function as part of user configuration. // If the drive_id does not exist, a new Block Device Config is added to the list. @@ -474,7 +441,7 @@ impl VmResources { /// /// If vhost-user-blk devices are in use, allocates memfd-backed shared memory, otherwise /// prefers anonymous memory for performance reasons. - pub fn allocate_guest_memory(&self) -> Result { + pub fn allocate_guest_memory(&self) -> Result, MemoryError> { let vhost_user_device_used = self .block .devices @@ -490,18 +457,19 @@ impl VmResources { // because that would require running a backend process. If in the future we converge to // a single way of backing guest memory for vhost-user and non-vhost-user cases, // that would not be worth the effort. + let regions = + crate::arch::arch_memory_regions(0, mib_to_bytes(self.machine_config.mem_size_mib)); if vhost_user_device_used { - GuestMemoryMmap::memfd_backed( - self.vm_config.mem_size_mib, - self.vm_config.track_dirty_pages, - self.vm_config.huge_pages, + memory::memfd_backed( + regions.as_ref(), + self.machine_config.track_dirty_pages, + self.machine_config.huge_pages, ) } else { - let regions = crate::arch::arch_memory_regions(self.vm_config.mem_size_mib << 20); - GuestMemoryMmap::from_raw_regions( - ®ions, - self.vm_config.track_dirty_pages, - self.vm_config.huge_pages, + memory::anonymous( + regions.into_iter(), + self.machine_config.track_dirty_pages, + self.machine_config.huge_pages, ) } } @@ -510,17 +478,17 @@ impl VmResources { impl From<&VmResources> for VmmConfig { fn from(resources: &VmResources) -> Self { VmmConfig { - balloon_device: resources.balloon.get_config().ok(), - block_devices: resources.block.configs(), - boot_source: resources.boot_source_config().clone(), + balloon: resources.balloon.get_config().ok(), + drives: resources.block.configs(), + boot_source: resources.boot_source.config.clone(), cpu_config: None, logger: None, - machine_config: Some(MachineConfig::from(&resources.vm_config)), + machine_config: Some(resources.machine_config.clone()), metrics: None, mmds_config: resources.mmds_config(), - net_devices: resources.net_builder.configs(), - vsock_device: resources.vsock.config(), - entropy_device: resources.entropy.config(), + network_interfaces: resources.net_builder.configs(), + vsock: resources.vsock.config(), + entropy: resources.entropy.config(), } } } @@ -536,6 +504,7 @@ mod tests { use vmm_sys_util::tempfile::TempFile; use super::*; + use crate::HTTP_MAX_PAYLOAD_SIZE; use crate::cpu_config::templates::{CpuTemplateType, StaticCpuTemplate}; use crate::devices::virtio::balloon::Balloon; use crate::devices::virtio::block::virtio::VirtioBlockError; @@ -543,15 +512,14 @@ mod tests { use crate::devices::virtio::vsock::VSOCK_DEV_ID; use crate::resources::VmResources; use crate::utils::net::mac::MacAddr; + use crate::vmm_config::RateLimiterConfig; use crate::vmm_config::boot_source::{ BootConfig, BootSource, BootSourceConfig, DEFAULT_KERNEL_CMDLINE, }; use crate::vmm_config::drive::{BlockBuilder, BlockDeviceConfig}; - use crate::vmm_config::machine_config::{HugePageConfig, MachineConfig, VmConfigError}; + use crate::vmm_config::machine_config::{HugePageConfig, MachineConfig, MachineConfigError}; use crate::vmm_config::net::{NetBuilder, NetworkInterfaceConfig}; use crate::vmm_config::vsock::tests::default_config; - use crate::vmm_config::RateLimiterConfig; - use crate::HTTP_MAX_PAYLOAD_SIZE; fn default_net_cfg() -> NetworkInterfaceConfig { NetworkInterfaceConfig { @@ -620,7 +588,7 @@ mod tests { fn default_vm_resources() -> VmResources { VmResources { - vm_config: VmConfig::default(), + machine_config: MachineConfig::default(), boot_source: default_boot_cfg(), block: default_blocks(), vsock: Default::default(), @@ -633,28 +601,6 @@ mod tests { } } - impl PartialEq for BootConfig { - fn eq(&self, other: &Self) -> bool { - self.cmdline.eq(&other.cmdline) - && self.kernel_file.metadata().unwrap().st_ino() - == other.kernel_file.metadata().unwrap().st_ino() - && self - .initrd_file - .as_ref() - .unwrap() - .metadata() - .unwrap() - .st_ino() - == other - .initrd_file - .as_ref() - .unwrap() - .metadata() - .unwrap() - .st_ino() - } - } - #[test] fn test_from_json() { let kernel_file = TempFile::new().unwrap(); @@ -833,7 +779,7 @@ mod tests { assert!( matches!( error, - ResourcesError::VmConfig(VmConfigError::InvalidMemorySize) + ResourcesError::MachineConfig(MachineConfigError::InvalidMemorySize) ), "{:?}", error @@ -1147,7 +1093,7 @@ mod tests { ) .unwrap(); assert_eq!( - vm_resources.vm_config.cpu_template, + vm_resources.machine_config.cpu_template, Some(CpuTemplateType::Custom(CustomCpuTemplate::default())) ); } @@ -1351,7 +1297,7 @@ mod tests { } #[test] - fn test_update_vm_config() { + fn test_update_machine_config() { let mut vm_resources = default_vm_resources(); let mut aux_vm_config = MachineConfigUpdate { vcpu_count: Some(32), @@ -1363,28 +1309,30 @@ mod tests { cpu_template: Some(StaticCpuTemplate::V1N1), track_dirty_pages: Some(false), huge_pages: Some(HugePageConfig::None), + #[cfg(feature = "gdb")] + gdb_socket_path: None, }; assert_ne!( - MachineConfigUpdate::from(MachineConfig::from(&vm_resources.vm_config)), + MachineConfigUpdate::from(vm_resources.machine_config.clone()), aux_vm_config ); - vm_resources.update_vm_config(&aux_vm_config).unwrap(); + vm_resources.update_machine_config(&aux_vm_config).unwrap(); assert_eq!( - MachineConfigUpdate::from(MachineConfig::from(&vm_resources.vm_config)), + MachineConfigUpdate::from(vm_resources.machine_config.clone()), aux_vm_config ); // Invalid vcpu count. aux_vm_config.vcpu_count = Some(0); assert_eq!( - vm_resources.update_vm_config(&aux_vm_config), - Err(VmConfigError::InvalidVcpuCount) + vm_resources.update_machine_config(&aux_vm_config), + Err(MachineConfigError::InvalidVcpuCount) ); aux_vm_config.vcpu_count = Some(33); assert_eq!( - vm_resources.update_vm_config(&aux_vm_config), - Err(VmConfigError::InvalidVcpuCount) + vm_resources.update_machine_config(&aux_vm_config), + Err(MachineConfigError::InvalidVcpuCount) ); // Check that SMT is not supported on aarch64, and that on x86_64 enabling it requires vcpu @@ -1392,29 +1340,29 @@ mod tests { aux_vm_config.smt = Some(true); #[cfg(target_arch = "aarch64")] assert_eq!( - vm_resources.update_vm_config(&aux_vm_config), - Err(VmConfigError::SmtNotSupported) + vm_resources.update_machine_config(&aux_vm_config), + Err(MachineConfigError::SmtNotSupported) ); aux_vm_config.vcpu_count = Some(3); #[cfg(target_arch = "x86_64")] assert_eq!( - vm_resources.update_vm_config(&aux_vm_config), - Err(VmConfigError::InvalidVcpuCount) + vm_resources.update_machine_config(&aux_vm_config), + Err(MachineConfigError::InvalidVcpuCount) ); aux_vm_config.vcpu_count = Some(32); #[cfg(target_arch = "x86_64")] - vm_resources.update_vm_config(&aux_vm_config).unwrap(); + vm_resources.update_machine_config(&aux_vm_config).unwrap(); aux_vm_config.smt = Some(false); // Invalid mem_size_mib. aux_vm_config.mem_size_mib = Some(0); assert_eq!( - vm_resources.update_vm_config(&aux_vm_config), - Err(VmConfigError::InvalidMemorySize) + vm_resources.update_machine_config(&aux_vm_config), + Err(MachineConfigError::InvalidMemorySize) ); // Incompatible mem_size_mib with balloon size. - vm_resources.vm_config.mem_size_mib = 128; + vm_resources.machine_config.mem_size_mib = 128; vm_resources .set_balloon_device(BalloonDeviceConfig { amount_mib: 100, @@ -1424,20 +1372,22 @@ mod tests { .unwrap(); aux_vm_config.mem_size_mib = Some(90); assert_eq!( - vm_resources.update_vm_config(&aux_vm_config), - Err(VmConfigError::IncompatibleBalloonSize) + vm_resources.update_machine_config(&aux_vm_config), + Err(MachineConfigError::IncompatibleBalloonSize) ); // mem_size_mib compatible with balloon size. aux_vm_config.mem_size_mib = Some(256); - vm_resources.update_vm_config(&aux_vm_config).unwrap(); + vm_resources.update_machine_config(&aux_vm_config).unwrap(); // mem_size_mib incompatible with huge pages configuration aux_vm_config.mem_size_mib = Some(129); aux_vm_config.huge_pages = Some(HugePageConfig::Hugetlbfs2M); assert_eq!( - vm_resources.update_vm_config(&aux_vm_config).unwrap_err(), - VmConfigError::InvalidMemorySize + vm_resources + .update_machine_config(&aux_vm_config) + .unwrap_err(), + MachineConfigError::InvalidMemorySize ); // mem_size_mib compatible with huge page configuration @@ -1445,7 +1395,7 @@ mod tests { // Remove the balloon device config that's added by `default_vm_resources` as it would // trigger the "ballooning incompatible with huge pages" check. vm_resources.balloon = BalloonBuilder::new(); - vm_resources.update_vm_config(&aux_vm_config).unwrap(); + vm_resources.update_machine_config(&aux_vm_config).unwrap(); } #[test] @@ -1486,7 +1436,7 @@ mod tests { let mut vm_resources = default_vm_resources(); vm_resources.balloon = BalloonBuilder::new(); vm_resources - .update_vm_config(&MachineConfigUpdate { + .update_machine_config(&MachineConfigUpdate { huge_pages: Some(HugePageConfig::Hugetlbfs2M), ..Default::default() }) @@ -1521,15 +1471,6 @@ mod tests { assert_eq!(actual_entropy_cfg, entropy_device_cfg); } - #[test] - fn test_boot_config() { - let vm_resources = default_vm_resources(); - let expected_boot_cfg = vm_resources.boot_source.builder.as_ref().unwrap(); - let actual_boot_cfg = vm_resources.boot_source_builder().unwrap(); - - assert!(actual_boot_cfg == expected_boot_cfg); - } - #[test] fn test_set_boot_source() { let tmp_file = TempFile::new().unwrap(); @@ -1541,7 +1482,7 @@ mod tests { }; let mut vm_resources = default_vm_resources(); - let boot_builder = vm_resources.boot_source_builder().unwrap(); + let boot_builder = vm_resources.boot_source.builder.as_ref().unwrap(); let tmp_ino = tmp_file.as_file().metadata().unwrap().st_ino(); assert_ne!( @@ -1550,7 +1491,7 @@ mod tests { .as_cstring() .unwrap() .as_bytes_with_nul(), - [cmdline.as_bytes(), &[b'\0']].concat() + [cmdline.as_bytes(), b"\0"].concat() ); assert_ne!( boot_builder.kernel_file.metadata().unwrap().st_ino(), @@ -1568,14 +1509,14 @@ mod tests { ); vm_resources.build_boot_source(expected_boot_cfg).unwrap(); - let boot_source_builder = vm_resources.boot_source_builder().unwrap(); + let boot_source_builder = vm_resources.boot_source.builder.unwrap(); assert_eq!( boot_source_builder .cmdline .as_cstring() .unwrap() .as_bytes_with_nul(), - [cmdline.as_bytes(), &[b'\0']].concat() + [cmdline.as_bytes(), b"\0"].concat() ); assert_eq!( boot_source_builder.kernel_file.metadata().unwrap().st_ino(), diff --git a/src/vmm/src/rpc_interface.rs b/src/vmm/src/rpc_interface.rs index 566228fd53a..127b75e594e 100644 --- a/src/vmm/src/rpc_interface.rs +++ b/src/vmm/src/rpc_interface.rs @@ -4,20 +4,21 @@ use std::fmt::{self, Debug}; use std::sync::{Arc, Mutex, MutexGuard}; -use seccompiler::BpfThreadMap; use serde_json::Value; -use utils::time::{get_time_us, ClockType}; +use utils::time::{ClockType, get_time_us}; use super::builder::build_and_boot_microvm; use super::persist::{create_snapshot, restore_from_snapshot}; use super::resources::VmResources; use super::{Vmm, VmmError}; +use crate::EventManager; use crate::builder::StartMicrovmError; use crate::cpu_config::templates::{CustomCpuTemplate, GuestConfigError}; -use crate::logger::{info, warn, LoggerConfig, *}; +use crate::logger::{LoggerConfig, info, warn, *}; use crate::mmds::data_store::{self, Mmds}; use crate::persist::{CreateSnapshotError, RestoreFromSnapshotError, VmInfo}; use crate::resources::VmmConfig; +use crate::seccomp::BpfThreadMap; use crate::vmm_config::balloon::{ BalloonConfigError, BalloonDeviceConfig, BalloonStats, BalloonUpdateConfig, BalloonUpdateStatsConfig, @@ -26,7 +27,7 @@ use crate::vmm_config::boot_source::{BootSourceConfig, BootSourceConfigError}; use crate::vmm_config::drive::{BlockDeviceConfig, BlockDeviceUpdateConfig, DriveError}; use crate::vmm_config::entropy::{EntropyDeviceConfig, EntropyDeviceError}; use crate::vmm_config::instance_info::InstanceInfo; -use crate::vmm_config::machine_config::{MachineConfig, MachineConfigUpdate, VmConfigError}; +use crate::vmm_config::machine_config::{MachineConfig, MachineConfigError, MachineConfigUpdate}; use crate::vmm_config::metrics::{MetricsConfig, MetricsConfigError}; use crate::vmm_config::mmds::{MmdsConfig, MmdsConfigError}; use crate::vmm_config::net::{ @@ -35,7 +36,6 @@ use crate::vmm_config::net::{ use crate::vmm_config::snapshot::{CreateSnapshotParams, LoadSnapshotParams, SnapshotType}; use crate::vmm_config::vsock::{VsockConfigError, VsockDeviceConfig}; use crate::vmm_config::{self, RateLimiterUpdate}; -use crate::EventManager; /// This enum represents the public interface of the VMM. Each action contains various /// bits of information (ids, paths, etc.). @@ -120,7 +120,7 @@ pub enum VmmAction { UpdateNetworkInterface(NetworkInterfaceUpdateConfig), /// Update the microVM configuration (memory & vcpu) using `VmUpdateConfig` as input. This /// action can only be called before the microVM has booted. - UpdateVmConfiguration(MachineConfigUpdate), + UpdateMachineConfiguration(MachineConfigUpdate), } /// Wrapper for all errors associated with VMM actions. @@ -145,7 +145,7 @@ pub enum VmmActionError { /// Logger error: {0} Logger(#[from] crate::logger::LoggerUpdateError), /// Machine config error: {0} - MachineConfig(#[from] VmConfigError), + MachineConfig(#[from] MachineConfigError), /// Metrics error: {0} Metrics(#[from] MetricsConfigError), #[from(ignore)] @@ -249,7 +249,7 @@ pub struct PrebootApiController<'a> { } // TODO Remove when `EventManager` implements `std::fmt::Debug`. -impl<'a> fmt::Debug for PrebootApiController<'a> { +impl fmt::Debug for PrebootApiController<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PrebootApiController") .field("seccomp_filters", &self.seccomp_filters) @@ -415,9 +415,9 @@ impl<'a> PrebootApiController<'a> { Ok(VmmData::FullVmConfig((&*self.vm_resources).into())) } GetMMDS => self.get_mmds(), - GetVmMachineConfig => Ok(VmmData::MachineConfiguration(MachineConfig::from( - &self.vm_resources.vm_config, - ))), + GetVmMachineConfig => Ok(VmmData::MachineConfiguration( + self.vm_resources.machine_config.clone(), + )), GetVmInstanceInfo => Ok(VmmData::InstanceInformation(self.instance_info.clone())), GetVmmVersion => Ok(VmmData::VmmVersion(self.instance_info.vmm_version.clone())), InsertBlockDevice(config) => self.insert_block_device(config), @@ -434,7 +434,7 @@ impl<'a> PrebootApiController<'a> { SetVsockDevice(config) => self.set_vsock_device(config), SetMmdsConfiguration(config) => self.set_mmds_config(config), StartMicroVm => self.start_microvm(), - UpdateVmConfiguration(config) => self.update_vm_config(config), + UpdateMachineConfiguration(config) => self.update_machine_config(config), SetEntropyDevice(config) => self.set_entropy_device(config), // Operations not allowed pre-boot. CreateSnapshot(_) @@ -502,10 +502,13 @@ impl<'a> PrebootApiController<'a> { .map_err(VmmActionError::MmdsConfig) } - fn update_vm_config(&mut self, cfg: MachineConfigUpdate) -> Result { + fn update_machine_config( + &mut self, + cfg: MachineConfigUpdate, + ) -> Result { self.boot_path = true; self.vm_resources - .update_vm_config(&cfg) + .update_machine_config(&cfg) .map(|()| VmmData::Empty) .map_err(VmmActionError::MachineConfig) } @@ -572,20 +575,18 @@ impl<'a> PrebootApiController<'a> { load_params, self.vm_resources, ) - .map_err(|err| { + .inspect_err(|_| { // If restore fails, we consider the process is too dirty to recover. self.fatal_error = Some(BuildMicrovmFromRequestsError::Restore); - err })?; // Resume VM if load_params.resume_vm { vmm.lock() .expect("Poisoned lock") .resume_vm() - .map_err(|err| { + .inspect_err(|_| { // If resume fails, we consider the process is too dirty to recover. self.fatal_error = Some(BuildMicrovmFromRequestsError::Resume); - err })?; } // Set the VM @@ -643,9 +644,9 @@ impl RuntimeApiController { .map_err(|err| VmmActionError::BalloonConfig(BalloonConfigError::from(err))), GetFullVmConfig => Ok(VmmData::FullVmConfig((&self.vm_resources).into())), GetMMDS => self.get_mmds(), - GetVmMachineConfig => Ok(VmmData::MachineConfiguration(MachineConfig::from( - &self.vm_resources.vm_config, - ))), + GetVmMachineConfig => Ok(VmmData::MachineConfiguration( + self.vm_resources.machine_config.clone(), + )), GetVmInstanceInfo => Ok(VmmData::InstanceInformation( self.vmm.lock().expect("Poisoned lock").instance_info(), )), @@ -688,7 +689,7 @@ impl RuntimeApiController { | SetMmdsConfiguration(_) | SetEntropyDevice(_) | StartMicroVm - | UpdateVmConfiguration(_) => Err(VmmActionError::OperationNotSupportedPostBoot), + | UpdateMachineConfiguration(_) => Err(VmmActionError::OperationNotSupportedPostBoot), } } @@ -755,7 +756,7 @@ impl RuntimeApiController { log_dev_preview_warning("Virtual machine snapshots", None); if create_params.snapshot_type == SnapshotType::Diff - && !self.vm_resources.vm_config.track_dirty_pages + && !self.vm_resources.machine_config.track_dirty_pages { return Err(VmmActionError::NotSupported( "Diff snapshots are not allowed on uVMs with dirty page tracking disabled." @@ -807,14 +808,12 @@ impl RuntimeApiController { // vhost-user-block updates if new_cfg.path_on_host.is_none() && new_cfg.rate_limiter.is_none() { vmm.update_vhost_user_block_config(&new_cfg.drive_id) - .map(|()| VmmData::Empty) .map_err(DriveError::DeviceUpdate)?; } // virtio-block updates if let Some(new_path) = new_cfg.path_on_host { vmm.update_block_device_path(&new_cfg.drive_id, new_path) - .map(|()| VmmData::Empty) .map_err(DriveError::DeviceUpdate)?; } if new_cfg.rate_limiter.is_some() { @@ -823,7 +822,6 @@ impl RuntimeApiController { RateLimiterUpdate::from(new_cfg.rate_limiter).bandwidth, RateLimiterUpdate::from(new_cfg.rate_limiter).ops, ) - .map(|()| VmmData::Empty) .map_err(DriveError::DeviceUpdate)?; } Ok(VmmData::Empty) @@ -854,14 +852,13 @@ impl RuntimeApiController { mod tests { use std::path::PathBuf; - use seccompiler::BpfThreadMap; - use super::*; + use crate::HTTP_MAX_PAYLOAD_SIZE; use crate::builder::tests::default_vmm; use crate::devices::virtio::block::CacheType; use crate::mmds::data_store::MmdsVersion; + use crate::seccomp::BpfThreadMap; use crate::vmm_config::snapshot::{MemBackendConfig, MemBackendType}; - use crate::HTTP_MAX_PAYLOAD_SIZE; fn default_preboot<'a>( vm_resources: &'a mut VmResources, @@ -1257,7 +1254,7 @@ mod tests { network_interfaces: Vec::new(), }, ))); - check_unsupported(runtime_request(VmmAction::UpdateVmConfiguration( + check_unsupported(runtime_request(VmmAction::UpdateMachineConfiguration( MachineConfigUpdate::from(MachineConfig::default()), ))); check_unsupported(runtime_request(VmmAction::LoadSnapshot( @@ -1269,6 +1266,7 @@ mod tests { }, enable_diff_snapshots: false, resume_vm: false, + network_overrides: vec![], }, ))); check_unsupported(runtime_request(VmmAction::SetEntropyDevice( diff --git a/src/vmm/src/seccomp.rs b/src/vmm/src/seccomp.rs new file mode 100644 index 00000000000..56e30908a62 --- /dev/null +++ b/src/vmm/src/seccomp.rs @@ -0,0 +1,215 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::io::Read; +use std::sync::Arc; + +use bincode::config; +use bincode::config::{Configuration, Fixint, Limit, LittleEndian}; + +// This byte limit is passed to `bincode` to guard against a potential memory +// allocation DOS caused by binary filters that are too large. +// This limit can be safely determined since the maximum length of a BPF +// filter is 4096 instructions and Firecracker has a finite number of threads. +const DESERIALIZATION_BYTES_LIMIT: usize = 100_000; + +const BINCODE_CONFIG: Configuration> = + config::standard() + .with_fixed_int_encoding() + .with_limit::() + .with_little_endian(); + +/// Each BPF instruction is 8 bytes long and 4 byte aligned. +/// This alignment needs to be satisfied in order for a BPF code to be accepted +/// by the syscalls. Using u64 here is is safe as it has same size and even bigger alignment. +pub type BpfInstruction = u64; + +/// Program made up of a sequence of BPF instructions. +pub type BpfProgram = Vec; + +/// Reference to program made up of a sequence of BPF instructions. +pub type BpfProgramRef<'a> = &'a [BpfInstruction]; + +/// Type that associates a thread category to a BPF program. +pub type BpfThreadMap = HashMap>; + +/// Binary filter deserialization errors. +pub type DeserializationError = bincode::error::DecodeError; + +/// Retrieve empty seccomp filters. +pub fn get_empty_filters() -> BpfThreadMap { + let mut map = BpfThreadMap::new(); + map.insert("vmm".to_string(), Arc::new(vec![])); + map.insert("api".to_string(), Arc::new(vec![])); + map.insert("vcpu".to_string(), Arc::new(vec![])); + map +} + +/// Deserialize binary with bpf filters +pub fn deserialize_binary(mut reader: R) -> Result { + let result: HashMap = bincode::decode_from_std_read(&mut reader, BINCODE_CONFIG)?; + + Ok(result + .into_iter() + .map(|(k, v)| (k.to_lowercase(), Arc::new(v))) + .collect()) +} + +/// Filter installation errors. +#[derive(Debug, thiserror::Error, displaydoc::Display)] +pub enum InstallationError { + /// Filter length exceeds the maximum size of {BPF_MAX_LEN:} instructions + FilterTooLarge, + /// prctl` syscall failed with error code: {0} + Prctl(std::io::Error), +} + +/// The maximum seccomp-BPF program length allowed by the linux kernel. +pub const BPF_MAX_LEN: usize = 4096; + +/// BPF structure definition for filter array. +/// See /usr/include/linux/filter.h . +#[repr(C)] +#[derive(Debug)] +struct SockFprog { + len: u16, + filter: *const BpfInstruction, +} + +/// Apply bpf filter. +pub fn apply_filter(bpf_filter: BpfProgramRef) -> Result<(), InstallationError> { + // If the program is empty, don't install the filter. + if bpf_filter.is_empty() { + return Ok(()); + } + + // If the program length is greater than the limit allowed by the kernel, + // fail quickly. Otherwise, `prctl` will give a more cryptic error code. + if BPF_MAX_LEN < bpf_filter.len() { + return Err(InstallationError::FilterTooLarge); + } + + let bpf_filter_len = + u16::try_from(bpf_filter.len()).map_err(|_| InstallationError::FilterTooLarge)?; + + // SAFETY: Safe because the parameters are valid. + unsafe { + { + let rc = libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + if rc != 0 { + return Err(InstallationError::Prctl(std::io::Error::last_os_error())); + } + } + + let bpf_prog = SockFprog { + len: bpf_filter_len, + filter: bpf_filter.as_ptr(), + }; + let bpf_prog_ptr = &bpf_prog as *const SockFprog; + { + let rc = libc::syscall( + libc::SYS_seccomp, + libc::SECCOMP_SET_MODE_FILTER, + 0, + bpf_prog_ptr, + ); + if rc != 0 { + return Err(InstallationError::Prctl(std::io::Error::last_os_error())); + } + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + #![allow(clippy::undocumented_unsafe_blocks)] + + use std::collections::HashMap; + use std::sync::Arc; + use std::thread; + + use super::*; + + #[test] + fn test_deserialize_binary() { + // Malformed bincode binary. + let data = "adassafvc".to_string(); + deserialize_binary(data.as_bytes()).unwrap_err(); + + // Test that the binary deserialization is correct, and that the thread keys + // have been lowercased. + let bpf_prog = vec![0; 2]; + let mut filter_map: HashMap = HashMap::new(); + filter_map.insert("VcpU".to_string(), bpf_prog.clone()); + let bytes = bincode::serde::encode_to_vec(&filter_map, BINCODE_CONFIG).unwrap(); + + let mut expected_res = BpfThreadMap::new(); + expected_res.insert("vcpu".to_string(), Arc::new(bpf_prog)); + assert_eq!(deserialize_binary(&bytes[..]).unwrap(), expected_res); + + let bpf_prog = vec![0; DESERIALIZATION_BYTES_LIMIT + 1]; + let mut filter_map: HashMap = HashMap::new(); + filter_map.insert("VcpU".to_string(), bpf_prog.clone()); + let bytes = bincode::serde::encode_to_vec(&filter_map, BINCODE_CONFIG).unwrap(); + assert!(matches!( + deserialize_binary(&bytes[..]).unwrap_err(), + bincode::error::DecodeError::LimitExceeded + )); + } + + #[test] + fn test_filter_apply() { + // Test filter too large. + thread::spawn(|| { + let filter: BpfProgram = vec![0; 5000]; + + // Apply seccomp filter. + assert!(matches!( + apply_filter(&filter).unwrap_err(), + InstallationError::FilterTooLarge + )); + }) + .join() + .unwrap(); + + // Test empty filter. + thread::spawn(|| { + let filter: BpfProgram = vec![]; + + assert_eq!(filter.len(), 0); + + let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) }; + assert_eq!(seccomp_level, 0); + + apply_filter(&filter).unwrap(); + + // test that seccomp level remains 0 on failure. + let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) }; + assert_eq!(seccomp_level, 0); + }) + .join() + .unwrap(); + + // Test invalid BPF code. + thread::spawn(|| { + let filter = vec![0xFF; 1]; + + let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) }; + assert_eq!(seccomp_level, 0); + + assert!(matches!( + apply_filter(&filter).unwrap_err(), + InstallationError::Prctl(_) + )); + + // test that seccomp level remains 0 on failure. + let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) }; + assert_eq!(seccomp_level, 0); + }) + .join() + .unwrap(); + } +} diff --git a/src/vmm/src/seccomp_filters.rs b/src/vmm/src/seccomp_filters.rs deleted file mode 100644 index aabdc1ef2c1..00000000000 --- a/src/vmm/src/seccomp_filters.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -use std::sync::Arc; - -use seccompiler::BpfThreadMap; - -/// Retrieve empty seccomp filters. -pub fn get_empty_filters() -> BpfThreadMap { - let mut map = BpfThreadMap::new(); - map.insert("vmm".to_string(), Arc::new(vec![])); - map.insert("api".to_string(), Arc::new(vec![])); - map.insert("vcpu".to_string(), Arc::new(vec![])); - map -} diff --git a/src/vmm/src/signal_handler.rs b/src/vmm/src/signal_handler.rs index 5bcfd41fd06..3b6162c7e4c 100644 --- a/src/vmm/src/signal_handler.rs +++ b/src/vmm/src/signal_handler.rs @@ -2,13 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use libc::{ - c_int, c_void, siginfo_t, SIGBUS, SIGHUP, SIGILL, SIGPIPE, SIGSEGV, SIGSYS, SIGXCPU, SIGXFSZ, + SIGBUS, SIGHUP, SIGILL, SIGPIPE, SIGSEGV, SIGSYS, SIGXCPU, SIGXFSZ, c_int, c_void, siginfo_t, }; use log::error; -use crate::logger::{IncMetric, StoreMetric, METRICS}; -use crate::utils::signal::register_signal_handler; use crate::FcExitCode; +use crate::logger::{IncMetric, METRICS, StoreMetric}; +use crate::utils::signal::register_signal_handler; // The offset of `si_syscall` (offending syscall identifier) within the siginfo structure // expressed as an `(u)int*`. @@ -177,7 +177,6 @@ mod tests { use std::{process, thread}; use libc::syscall; - use seccompiler::sock_filter; use super::*; @@ -186,11 +185,6 @@ mod tests { let child = thread::spawn(move || { register_signal_handlers().unwrap(); - let filter = make_test_seccomp_bpf_filter(); - - seccompiler::apply_filter(&filter).unwrap(); - assert_eq!(METRICS.seccomp.num_faults.fetch(), 0); - // Call the forbidden `SYS_mkdirat`. unsafe { libc::syscall(libc::SYS_mkdirat, "/foo/bar\0") }; @@ -238,7 +232,6 @@ mod tests { }); child.join().unwrap(); - assert!(METRICS.seccomp.num_faults.fetch() >= 1); assert!(METRICS.signals.sigbus.fetch() >= 1); assert!(METRICS.signals.sigsegv.fetch() >= 1); assert!(METRICS.signals.sigxfsz.fetch() >= 1); @@ -247,141 +240,4 @@ mod tests { assert!(METRICS.signals.sighup.fetch() >= 1); assert!(METRICS.signals.sigill.fetch() >= 1); } - - fn make_test_seccomp_bpf_filter() -> Vec { - // Create seccomp filter that allows all syscalls, except for `SYS_mkdirat`. - // For some reason, directly calling `SYS_kill` with SIGSYS, like we do with the - // other signals, results in an error. Probably because of the way `cargo test` is - // handling signals. - #[cfg(target_arch = "aarch64")] - #[allow(clippy::unreadable_literal)] - let bpf_filter = vec![ - sock_filter { - code: 32, - jt: 0, - jf: 0, - k: 4, - }, - sock_filter { - code: 21, - jt: 1, - jf: 0, - k: 3221225655, - }, - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: 0, - }, - sock_filter { - code: 32, - jt: 0, - jf: 0, - k: 0, - }, - sock_filter { - code: 21, - jt: 0, - jf: 1, - k: 34, - }, - sock_filter { - code: 5, - jt: 0, - jf: 0, - k: 1, - }, - sock_filter { - code: 5, - jt: 0, - jf: 0, - k: 2, - }, - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: 196608, - }, - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: 2147418112, - }, - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: 2147418112, - }, - ]; - #[cfg(target_arch = "x86_64")] - #[allow(clippy::unreadable_literal)] - let bpf_filter = vec![ - sock_filter { - code: 32, - jt: 0, - jf: 0, - k: 4, - }, - sock_filter { - code: 21, - jt: 1, - jf: 0, - k: 3221225534, - }, - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: 0, - }, - sock_filter { - code: 32, - jt: 0, - jf: 0, - k: 0, - }, - sock_filter { - code: 21, - jt: 0, - jf: 1, - k: 258, - }, - sock_filter { - code: 5, - jt: 0, - jf: 0, - k: 1, - }, - sock_filter { - code: 5, - jt: 0, - jf: 0, - k: 2, - }, - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: 196608, - }, - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: 2147418112, - }, - sock_filter { - code: 6, - jt: 0, - jf: 0, - k: 2147418112, - }, - ]; - - bpf_filter - } } diff --git a/src/vmm/src/snapshot/mod.rs b/src/vmm/src/snapshot/mod.rs index a4900ee6eff..57ad3980215 100644 --- a/src/vmm/src/snapshot/mod.rs +++ b/src/vmm/src/snapshot/mod.rs @@ -28,19 +28,27 @@ mod persist; use std::fmt::Debug; use std::io::{Read, Write}; -use bincode::Options; +use bincode::config; +use bincode::config::{Configuration, Fixint, Limit, LittleEndian}; use semver::Version; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use crate::snapshot::crc::{CRC64Reader, CRC64Writer}; pub use crate::snapshot::persist::Persist; +use crate::utils::mib_to_bytes; #[cfg(target_arch = "x86_64")] const SNAPSHOT_MAGIC_ID: u64 = 0x0710_1984_8664_0000u64; /// Constant bounding how much memory bincode may allocate during vmstate file deserialization -const VM_STATE_DESERIALIZE_LIMIT: u64 = 10_485_760; // 10MiB +const DESERIALIZATION_BYTES_LIMIT: usize = mib_to_bytes(10); + +const BINCODE_CONFIG: Configuration> = + config::standard() + .with_fixed_int_encoding() + .with_limit::() + .with_little_endian(); #[cfg(target_arch = "aarch64")] const SNAPSHOT_MAGIC_ID: u64 = 0x0710_1984_AAAA_0000u64; @@ -110,13 +118,7 @@ impl Snapshot { T: Read, O: DeserializeOwned + Debug, { - // flags below are those used by default by bincode::deserialize_from, plus `with_limit`. - bincode::DefaultOptions::new() - .with_limit(VM_STATE_DESERIALIZE_LIMIT) - .with_fixint_encoding() - .allow_trailing_bytes() // need this because we deserialize header and snapshot from the same file, so after - // reading the header, there will be trailing bytes. - .deserialize_from(reader) + bincode::serde::decode_from_std_read(reader, BINCODE_CONFIG) .map_err(|err| SnapshotError::Serde(err.to_string())) } @@ -126,7 +128,10 @@ impl Snapshot { T: Write, O: Serialize + Debug, { - bincode::serialize_into(writer, data).map_err(|err| SnapshotError::Serde(err.to_string())) + bincode::serde::encode_into_std_write(data, writer, BINCODE_CONFIG) + .map_err(|err| SnapshotError::Serde(err.to_string()))?; + + Ok(()) } /// Attempts to load an existing snapshot without performing CRC or version validation. diff --git a/src/vmm/src/test_utils/mock_resources/mod.rs b/src/vmm/src/test_utils/mock_resources/mod.rs index 9f4406ab280..f8485bf9678 100644 --- a/src/vmm/src/test_utils/mock_resources/mod.rs +++ b/src/vmm/src/test_utils/mock_resources/mod.rs @@ -81,12 +81,12 @@ impl MockVmResources { pub fn with_vm_config(mut self, vm_config: MachineConfig) -> Self { let machine_config = MachineConfigUpdate::from(vm_config); - self.0.update_vm_config(&machine_config).unwrap(); + self.0.update_machine_config(&machine_config).unwrap(); self } pub fn set_cpu_template(&mut self, cpu_template: CustomCpuTemplate) { - self.0.vm_config.set_custom_cpu_template(cpu_template); + self.0.machine_config.set_custom_cpu_template(cpu_template); } } diff --git a/src/vmm/src/test_utils/mod.rs b/src/vmm/src/test_utils/mod.rs index f90ba7fbd0c..7cb16a2a213 100644 --- a/src/vmm/src/test_utils/mod.rs +++ b/src/vmm/src/test_utils/mod.rs @@ -10,12 +10,13 @@ use vmm_sys_util::tempdir::TempDir; use crate::builder::build_microvm_for_boot; use crate::resources::VmResources; -use crate::seccomp_filters::get_empty_filters; +use crate::seccomp::get_empty_filters; use crate::test_utils::mock_resources::{MockBootSourceConfig, MockVmConfig, MockVmResources}; use crate::vmm_config::boot_source::BootSourceConfig; use crate::vmm_config::instance_info::InstanceInfo; use crate::vmm_config::machine_config::HugePageConfig; -use crate::vstate::memory::{GuestMemoryExtension, GuestMemoryMmap}; +use crate::vstate::memory; +use crate::vstate::memory::{GuestMemoryMmap, GuestRegionMmap}; use crate::{EventManager, Vmm}; pub mod mock_resources; @@ -26,22 +27,42 @@ pub fn single_region_mem(region_size: usize) -> GuestMemoryMmap { single_region_mem_at(0, region_size) } +pub fn single_region_mem_raw(region_size: usize) -> Vec { + single_region_mem_at_raw(0, region_size) +} + /// Creates a [`GuestMemoryMmap`] with a single region of the given size starting at the given /// guest physical address `at` and without dirty tracking. pub fn single_region_mem_at(at: u64, size: usize) -> GuestMemoryMmap { multi_region_mem(&[(GuestAddress(at), size)]) } +pub fn single_region_mem_at_raw(at: u64, size: usize) -> Vec { + multi_region_mem_raw(&[(GuestAddress(at), size)]) +} + /// Creates a [`GuestMemoryMmap`] with multiple regions and without dirty page tracking. pub fn multi_region_mem(regions: &[(GuestAddress, usize)]) -> GuestMemoryMmap { - GuestMemoryMmap::from_raw_regions(regions, false, HugePageConfig::None) + GuestMemoryMmap::from_regions( + memory::anonymous(regions.iter().copied(), false, HugePageConfig::None) + .expect("Cannot initialize memory"), + ) + .unwrap() +} + +pub fn multi_region_mem_raw(regions: &[(GuestAddress, usize)]) -> Vec { + memory::anonymous(regions.iter().copied(), false, HugePageConfig::None) .expect("Cannot initialize memory") } /// Creates a [`GuestMemoryMmap`] of the given size with the contained regions laid out in /// accordance with the requirements of the architecture on which the tests are being run. pub fn arch_mem(mem_size_bytes: usize) -> GuestMemoryMmap { - multi_region_mem(&crate::arch::arch_memory_regions(mem_size_bytes)) + multi_region_mem(&crate::arch::arch_memory_regions(0, mem_size_bytes)) +} + +pub fn arch_mem_raw(mem_size_bytes: usize) -> Vec { + multi_region_mem_raw(&crate::arch::arch_memory_regions(0, mem_size_bytes)) } pub fn create_vmm( @@ -117,7 +138,7 @@ pub fn create_tmp_socket() -> (TempDir, String) { std::ptr::copy( tmp_socket_path.as_ptr().cast(), socket_addr.sun_path.as_mut_ptr(), - tmp_socket_path.as_bytes().len(), + tmp_socket_path.len(), ); let bind = libc::bind( diff --git a/src/vmm/src/utils/byte_order.rs b/src/vmm/src/utils/byte_order.rs index 32aa9daee18..c0682c494df 100644 --- a/src/vmm/src/utils/byte_order.rs +++ b/src/vmm/src/utils/byte_order.rs @@ -5,13 +5,9 @@ macro_rules! generate_read_fn { ($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => { /// Read bytes from the slice pub fn $fn_name(input: &[$byte_type]) -> $data_type { - assert!($type_size == std::mem::size_of::<$data_type>()); - let mut array = [0u8; $type_size]; - #[allow(clippy::cast_sign_loss)] - #[allow(clippy::cast_possible_wrap)] - for (byte, read) in array.iter_mut().zip(input.iter().cloned()) { - *byte = read as u8; - } + let mut array = [0u8; std::mem::size_of::<$data_type>()]; + let how_many = input.len().min(std::mem::size_of::<$data_type>()); + array[..how_many].copy_from_slice(&input[..how_many]); <$data_type>::$endian_type(array) } }; @@ -21,32 +17,21 @@ macro_rules! generate_write_fn { ($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => { /// Write bytes to the slice pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) { - #[allow(clippy::cast_sign_loss)] - #[allow(clippy::cast_possible_wrap)] - for (byte, read) in buf - .iter_mut() - .zip(<$data_type>::$endian_type(n).iter().cloned()) - { - *byte = read as $byte_type; - } + let bytes = n.$endian_type(); + let how_much = buf.len().min(bytes.len()); + buf[..how_much].copy_from_slice(&bytes[..how_much]); } }; } -generate_read_fn!(read_le_u16, u16, u8, 2, from_le_bytes); generate_read_fn!(read_le_u32, u32, u8, 4, from_le_bytes); -generate_read_fn!(read_le_u32_from_i8, u32, i8, 4, from_le_bytes); generate_read_fn!(read_le_u64, u64, u8, 8, from_le_bytes); -generate_read_fn!(read_le_i32, i32, i8, 4, from_le_bytes); generate_read_fn!(read_be_u16, u16, u8, 2, from_be_bytes); generate_read_fn!(read_be_u32, u32, u8, 4, from_be_bytes); -generate_write_fn!(write_le_u16, u16, u8, to_le_bytes); generate_write_fn!(write_le_u32, u32, u8, to_le_bytes); -generate_write_fn!(write_le_u32_to_i8, u32, i8, to_le_bytes); generate_write_fn!(write_le_u64, u64, u8, to_le_bytes); -generate_write_fn!(write_le_i32, i32, i8, to_le_bytes); generate_write_fn!(write_be_u16, u16, u8, to_be_bytes); generate_write_fn!(write_be_u32, u32, u8, to_be_bytes); @@ -110,10 +95,8 @@ mod tests { }; } - byte_order_test_read_write!(test_le_u16, write_le_u16, read_le_u16, false, u16); byte_order_test_read_write!(test_le_u32, write_le_u32, read_le_u32, false, u32); byte_order_test_read_write!(test_le_u64, write_le_u64, read_le_u64, false, u64); - byte_order_test_read_write!(test_le_i32, write_le_i32, read_le_i32, false, i32); byte_order_test_read_write!(test_be_u16, write_be_u16, read_be_u16, true, u16); byte_order_test_read_write!(test_be_u32, write_be_u32, read_be_u32, true, u32); } diff --git a/src/vmm/src/utils/mod.rs b/src/vmm/src/utils/mod.rs index a0ee2e90b6b..430f9fe9a71 100644 --- a/src/vmm/src/utils/mod.rs +++ b/src/vmm/src/utils/mod.rs @@ -13,6 +13,9 @@ pub mod sm; use std::num::Wrapping; use std::result::Result; +/// How many bits to left-shift by to convert MiB to bytes +const MIB_TO_BYTES_SHIFT: usize = 20; + /// Return the default page size of the platform, in bytes. pub fn get_page_size() -> Result { // SAFETY: Safe because the parameters are valid. @@ -45,3 +48,20 @@ pub const fn usize_to_u64(num: usize) -> u64 { pub const fn wrap_usize_to_u32(num: usize) -> Wrapping { Wrapping(((num as u64) & 0xFFFFFFFF) as u32) } + +/// Converts MiB to Bytes +pub const fn mib_to_bytes(mib: usize) -> usize { + mib << MIB_TO_BYTES_SHIFT +} + +/// Align address up to the aligment. +pub const fn align_up(addr: u64, align: u64) -> u64 { + debug_assert!(align != 0); + (addr + align - 1) & !(align - 1) +} + +/// Align address down to the aligment. +pub const fn align_down(addr: u64, align: u64) -> u64 { + debug_assert!(align != 0); + addr & !(align - 1) +} diff --git a/src/vmm/src/utils/signal.rs b/src/vmm/src/utils/signal.rs index 172ae1b376e..5178c7ec3c1 100644 --- a/src/vmm/src/utils/signal.rs +++ b/src/vmm/src/utils/signal.rs @@ -4,19 +4,21 @@ use libc::c_int; pub use vmm_sys_util::signal::*; -extern "C" { - fn __libc_current_sigrtmin() -> c_int; - fn __libc_current_sigrtmax() -> c_int; +// SAFETY: these are valid libc functions +unsafe extern "C" { + // SAFETY: Function has no invariants that can be broken. + safe fn __libc_current_sigrtmin() -> c_int; + + // SAFETY: Function has no invariants that can be broken. + safe fn __libc_current_sigrtmax() -> c_int; } /// Sigrtmin pub fn sigrtmin() -> c_int { - // SAFETY: Function has no invariants that can be broken. - unsafe { __libc_current_sigrtmin() } + __libc_current_sigrtmin() } /// Sigrtmax pub fn sigrtmax() -> c_int { - // SAFETY: Function has no invariants that can be broken. - unsafe { __libc_current_sigrtmax() } + __libc_current_sigrtmax() } diff --git a/src/vmm/src/vmm_config/balloon.rs b/src/vmm/src/vmm_config/balloon.rs index 5b6f25e8662..6ac2fb34ecf 100644 --- a/src/vmm/src/vmm_config/balloon.rs +++ b/src/vmm/src/vmm_config/balloon.rs @@ -5,8 +5,8 @@ use std::sync::{Arc, Mutex}; use serde::{Deserialize, Serialize}; -pub use crate::devices::virtio::balloon::device::BalloonStats; pub use crate::devices::virtio::balloon::BALLOON_DEV_ID; +pub use crate::devices::virtio::balloon::device::BalloonStats; use crate::devices::virtio::balloon::{Balloon, BalloonConfig}; type MutexBalloon = Arc>; diff --git a/src/vmm/src/vmm_config/boot_source.rs b/src/vmm/src/vmm_config/boot_source.rs index c40a0fde014..37ba08be449 100644 --- a/src/vmm/src/vmm_config/boot_source.rs +++ b/src/vmm/src/vmm_config/boot_source.rs @@ -14,10 +14,9 @@ use serde::{Deserialize, Serialize}; /// - `8250.nr_uarts=0` disable 8250 serial interface; /// - `i8042.noaux` do not probe the i8042 controller for an attached mouse (save boot time); /// - `i8042.nomux` do not probe i8042 for a multiplexing controller (save boot time); -/// - `i8042.nopnp` do not use ACPIPnP to discover KBD/AUX controllers (save boot time); /// - `i8042.dumbkbd` do not attempt to control kbd state via the i8042 (save boot time). -pub const DEFAULT_KERNEL_CMDLINE: &str = "reboot=k panic=1 pci=off nomodule 8250.nr_uarts=0 \ - i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd"; +pub const DEFAULT_KERNEL_CMDLINE: &str = + "reboot=k panic=1 pci=off nomodule 8250.nr_uarts=0 i8042.noaux i8042.nomux i8042.dumbkbd"; /// Strongly typed data structure used to configure the boot source of the /// microvm. @@ -42,8 +41,6 @@ pub enum BootSourceConfigError { InvalidInitrdPath(io::Error), /// The kernel command line is invalid: {0} InvalidKernelCommandLine(String), - /// Firecracker's huge pages support is incompatible with initrds. - HugePagesAndInitRd, } /// Holds the kernel specification (both configuration as well as runtime details). @@ -119,7 +116,7 @@ pub(crate) mod tests { assert!(boot_cfg.initrd_file.is_none()); assert_eq!( boot_cfg.cmdline.as_cstring().unwrap().as_bytes_with_nul(), - [DEFAULT_KERNEL_CMDLINE.as_bytes(), &[b'\0']].concat() + [DEFAULT_KERNEL_CMDLINE.as_bytes(), b"\0"].concat() ); } diff --git a/src/vmm/src/vmm_config/drive.rs b/src/vmm/src/vmm_config/drive.rs index cdfc732d48b..9e301eff751 100644 --- a/src/vmm/src/vmm_config/drive.rs +++ b/src/vmm/src/vmm_config/drive.rs @@ -8,10 +8,10 @@ use std::sync::{Arc, Mutex}; use serde::{Deserialize, Serialize}; use super::RateLimiterConfig; +use crate::VmmError; use crate::devices::virtio::block::device::Block; pub use crate::devices::virtio::block::virtio::device::FileEngineType; use crate::devices::virtio::block::{BlockError, CacheType}; -use crate::VmmError; /// Errors associated with the operations allowed on a drive. #[derive(Debug, thiserror::Error, displaydoc::Display)] @@ -513,15 +513,19 @@ mod tests { ); // Get None. - assert!(block_devs - .get_index_of_drive_id(&String::from("foo")) - .is_none()); + assert!( + block_devs + .get_index_of_drive_id(&String::from("foo")) + .is_none() + ); // Test several update cases using dummy_block_device_2. // Validate `dummy_block_device_2` is already in the list - assert!(block_devs - .get_index_of_drive_id(&dummy_block_device_2.drive_id) - .is_some()); + assert!( + block_devs + .get_index_of_drive_id(&dummy_block_device_2.drive_id) + .is_some() + ); // Update OK. dummy_block_device_2.is_read_only = Some(true); block_devs.insert(dummy_block_device_2.clone()).unwrap(); diff --git a/src/vmm/src/vmm_config/instance_info.rs b/src/vmm/src/vmm_config/instance_info.rs index 67fd335deaa..cd5b44f30ba 100644 --- a/src/vmm/src/vmm_config/instance_info.rs +++ b/src/vmm/src/vmm_config/instance_info.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::fmt::{self, Display, Formatter}; -use serde::{ser, Serialize}; +use serde::{Serialize, ser}; /// Enumerates microVM runtime states. #[derive(Clone, Debug, Default, PartialEq, Eq)] diff --git a/src/vmm/src/vmm_config/machine_config.rs b/src/vmm/src/vmm_config/machine_config.rs index 8eee91c88be..cfe7105fdf8 100644 --- a/src/vmm/src/vmm_config/machine_config.rs +++ b/src/vmm/src/vmm_config/machine_config.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::fmt::Debug; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::cpu_config::templates::{CpuTemplateType, CustomCpuTemplate, StaticCpuTemplate}; @@ -15,7 +15,7 @@ pub const MAX_SUPPORTED_VCPUS: u8 = 32; /// Errors associated with configuring the microVM. #[rustfmt::skip] #[derive(Debug, thiserror::Error, displaydoc::Display, PartialEq, Eq)] -pub enum VmConfigError { +pub enum MachineConfigError { /// The memory size (MiB) is smaller than the previously set balloon device target size. IncompatibleBalloonSize, /// The memory size (MiB) is either 0, or not a multiple of the configured page size. @@ -31,8 +31,6 @@ pub enum VmConfigError { KernelVersion, /// Firecracker's huge pages support is incompatible with memory ballooning. BalloonAndHugePages, - /// Firecracker's huge pages support is incompatible with initrds. - InitrdAndHugePages, } /// Describes the possible (huge)page configurations for a microVM's memory. @@ -73,8 +71,8 @@ impl HugePageConfig { matches!(self, HugePageConfig::Hugetlbfs2M) } - /// Gets the page size in KiB of this [`HugePageConfig`]. - pub fn page_size_kib(&self) -> usize { + /// Gets the page size in bytes of this [`HugePageConfig`]. + pub fn page_size(&self) -> usize { match self { HugePageConfig::None => 4096, HugePageConfig::Hugetlbfs2M => 2 * 1024 * 1024, @@ -103,8 +101,14 @@ pub struct MachineConfig { #[serde(default)] pub smt: bool, /// A CPU template that it is used to filter the CPU features exposed to the guest. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub cpu_template: Option, + // FIXME: once support for static CPU templates is removed, this field can be dropped altogether + #[serde( + default, + skip_serializing_if = "is_none_or_custom_template", + deserialize_with = "deserialize_static_template", + serialize_with = "serialize_static_template" + )] + pub cpu_template: Option, /// Enables or disables dirty page tracking. Enabling allows incremental snapshots. #[serde(default)] pub track_dirty_pages: bool, @@ -117,42 +121,78 @@ pub struct MachineConfig { pub gdb_socket_path: Option, } +fn is_none_or_custom_template(template: &Option) -> bool { + matches!(template, None | Some(CpuTemplateType::Custom(_))) +} + +fn deserialize_static_template<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + Option::::deserialize(deserializer) + .map(|maybe_template| maybe_template.map(CpuTemplateType::Static)) +} + +fn serialize_static_template( + template: &Option, + serializer: S, +) -> Result +where + S: Serializer, +{ + let Some(CpuTemplateType::Static(template)) = template else { + // We have a skip_serializing_if on the field + unreachable!() + }; + + template.serialize(serializer) +} + impl Default for MachineConfig { fn default() -> Self { - Self::from(&VmConfig::default()) + Self { + vcpu_count: 1, + mem_size_mib: DEFAULT_MEM_SIZE_MIB, + smt: false, + cpu_template: None, + track_dirty_pages: false, + huge_pages: HugePageConfig::None, + #[cfg(feature = "gdb")] + gdb_socket_path: None, + } } } /// Struct used in PATCH `/machine-config` API call. -/// Used to update `VmConfig` in `VmResources`. +/// Used to update `MachineConfig` in `VmResources`. /// This struct mirrors all the fields in `MachineConfig`. /// All fields are optional, but at least one needs to be specified. /// If a field is `Some(value)` then we assume an update is requested /// for that field. -#[derive(Clone, Default, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Clone, Default, Debug, PartialEq, Eq, Deserialize)] #[serde(deny_unknown_fields)] pub struct MachineConfigUpdate { /// Number of vcpu to start. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] pub vcpu_count: Option, /// The memory size in MiB. - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] pub mem_size_mib: Option, /// Enables or disabled SMT. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] pub smt: Option, /// A CPU template that it is used to filter the CPU features exposed to the guest. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] pub cpu_template: Option, /// Enables or disables dirty page tracking. Enabling allows incremental snapshots. - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] pub track_dirty_pages: Option, /// Configures what page size Firecracker should use to back guest memory. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] pub huge_pages: Option, /// GDB socket address. #[cfg(feature = "gdb")] - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] pub gdb_socket_path: Option, } @@ -171,7 +211,7 @@ impl From for MachineConfigUpdate { vcpu_count: Some(cfg.vcpu_count), mem_size_mib: Some(cfg.mem_size_mib), smt: Some(cfg.smt), - cpu_template: cfg.cpu_template, + cpu_template: cfg.static_template(), track_dirty_pages: Some(cfg.track_dirty_pages), huge_pages: Some(cfg.huge_pages), #[cfg(feature = "gdb")] @@ -180,62 +220,52 @@ impl From for MachineConfigUpdate { } } -/// Configuration of the microvm. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct VmConfig { - /// Number of vcpu to start. - pub vcpu_count: u8, - /// The memory size in MiB. - pub mem_size_mib: usize, - /// Enables or disabled SMT. - pub smt: bool, - /// A CPU template that it is used to filter the CPU features exposed to the guest. - pub cpu_template: Option, - /// Enables or disables dirty page tracking. Enabling allows incremental snapshots. - pub track_dirty_pages: bool, - /// Configures what page size Firecracker should use to back guest memory. - pub huge_pages: HugePageConfig, - /// GDB socket address. - #[cfg(feature = "gdb")] - pub gdb_socket_path: Option, -} - -impl VmConfig { +impl MachineConfig { /// Sets cpu tempalte field to `CpuTemplateType::Custom(cpu_template)`. pub fn set_custom_cpu_template(&mut self, cpu_template: CustomCpuTemplate) { self.cpu_template = Some(CpuTemplateType::Custom(cpu_template)); } - /// Updates [`VmConfig`] with [`MachineConfigUpdate`]. + fn static_template(&self) -> Option { + match self.cpu_template { + Some(CpuTemplateType::Static(template)) => Some(template), + _ => None, + } + } + + /// Updates [`MachineConfig`] with [`MachineConfigUpdate`]. /// Mapping for cpu template update: /// StaticCpuTemplate::None -> None /// StaticCpuTemplate::Other -> Some(CustomCpuTemplate::Static(Other)), - /// Returns the updated `VmConfig` object. - pub fn update(&self, update: &MachineConfigUpdate) -> Result { + /// Returns the updated `MachineConfig` object. + pub fn update( + &self, + update: &MachineConfigUpdate, + ) -> Result { let vcpu_count = update.vcpu_count.unwrap_or(self.vcpu_count); let smt = update.smt.unwrap_or(self.smt); #[cfg(target_arch = "aarch64")] if smt { - return Err(VmConfigError::SmtNotSupported); + return Err(MachineConfigError::SmtNotSupported); } if vcpu_count == 0 || vcpu_count > MAX_SUPPORTED_VCPUS { - return Err(VmConfigError::InvalidVcpuCount); + return Err(MachineConfigError::InvalidVcpuCount); } // If SMT is enabled or is to be enabled in this call // only allow vcpu count to be 1 or even. if smt && vcpu_count > 1 && vcpu_count % 2 == 1 { - return Err(VmConfigError::InvalidVcpuCount); + return Err(MachineConfigError::InvalidVcpuCount); } let mem_size_mib = update.mem_size_mib.unwrap_or(self.mem_size_mib); let page_config = update.huge_pages.unwrap_or(self.huge_pages); if mem_size_mib == 0 || !page_config.is_valid_mem_size(mem_size_mib) { - return Err(VmConfigError::InvalidMemorySize); + return Err(MachineConfigError::InvalidMemorySize); } let cpu_template = match update.cpu_template { @@ -244,7 +274,7 @@ impl VmConfig { Some(other) => Some(CpuTemplateType::Static(other)), }; - Ok(VmConfig { + Ok(MachineConfig { vcpu_count, mem_size_mib, smt, @@ -257,32 +287,54 @@ impl VmConfig { } } -impl Default for VmConfig { - fn default() -> Self { - Self { - vcpu_count: 1, - mem_size_mib: DEFAULT_MEM_SIZE_MIB, - smt: false, +#[cfg(test)] +mod tests { + use crate::cpu_config::templates::{CpuTemplateType, CustomCpuTemplate, StaticCpuTemplate}; + use crate::vmm_config::machine_config::MachineConfig; + + // Ensure the special (de)serialization logic for the cpu_template field works: + // only static cpu templates can be specified via the machine-config endpoint, but + // we still cram custom cpu templates into the MachineConfig struct if they're set otherwise + // Ensure that during (de)serialization we preserve static templates, but we set custom + // templates to None + #[test] + fn test_serialize_machine_config() { + #[cfg(target_arch = "aarch64")] + const TEMPLATE: StaticCpuTemplate = StaticCpuTemplate::V1N1; + #[cfg(target_arch = "x86_64")] + const TEMPLATE: StaticCpuTemplate = StaticCpuTemplate::T2S; + + let mconfig = MachineConfig { cpu_template: None, - track_dirty_pages: false, - huge_pages: HugePageConfig::None, - #[cfg(feature = "gdb")] - gdb_socket_path: None, - } - } -} + ..Default::default() + }; -impl From<&VmConfig> for MachineConfig { - fn from(value: &VmConfig) -> Self { - Self { - vcpu_count: value.vcpu_count, - mem_size_mib: value.mem_size_mib, - smt: value.smt, - cpu_template: value.cpu_template.as_ref().map(|template| template.into()), - track_dirty_pages: value.track_dirty_pages, - huge_pages: value.huge_pages, - #[cfg(feature = "gdb")] - gdb_socket_path: value.gdb_socket_path.clone(), - } + let serialized = serde_json::to_string(&mconfig).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + assert!(deserialized.cpu_template.is_none()); + + let mconfig = MachineConfig { + cpu_template: Some(CpuTemplateType::Static(TEMPLATE)), + ..Default::default() + }; + + let serialized = serde_json::to_string(&mconfig).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + assert_eq!( + deserialized.cpu_template, + Some(CpuTemplateType::Static(TEMPLATE)) + ); + + let mconfig = MachineConfig { + cpu_template: Some(CpuTemplateType::Custom(CustomCpuTemplate::default())), + ..Default::default() + }; + + let serialized = serde_json::to_string(&mconfig).unwrap(); + let deserialized = serde_json::from_str::(&serialized).unwrap(); + + assert!(deserialized.cpu_template.is_none()); } } diff --git a/src/vmm/src/vmm_config/net.rs b/src/vmm/src/vmm_config/net.rs index 4b917c2ca48..f1413368090 100644 --- a/src/vmm/src/vmm_config/net.rs +++ b/src/vmm/src/vmm_config/net.rs @@ -8,9 +8,9 @@ use std::sync::{Arc, Mutex}; use serde::{Deserialize, Serialize}; use super::RateLimiterConfig; +use crate::VmmError; use crate::devices::virtio::net::{Net, TapError}; use crate::utils::net::mac::MacAddr; -use crate::VmmError; /// This struct represents the strongly typed equivalent of the json body from net iface /// related requests. @@ -183,13 +183,9 @@ mod tests { use crate::rate_limiter::RateLimiter; impl NetBuilder { - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.net_devices.len() } - - pub fn is_empty(&self) -> bool { - self.net_devices.len() == 0 - } } fn create_netif(id: &str, name: &str, mac: &str) -> NetworkInterfaceConfig { diff --git a/src/vmm/src/vmm_config/snapshot.rs b/src/vmm/src/vmm_config/snapshot.rs index e1850b74939..27a7841d5a4 100644 --- a/src/vmm/src/vmm_config/snapshot.rs +++ b/src/vmm/src/vmm_config/snapshot.rs @@ -47,6 +47,16 @@ pub struct CreateSnapshotParams { pub mem_file_path: PathBuf, } +/// Allows for changing the mapping between tap devices and host devices +/// during snapshot restore +#[derive(Debug, PartialEq, Eq, Deserialize)] +pub struct NetworkOverride { + /// The index of the interface to modify + pub iface_id: String, + /// The new name of the interface to be assigned + pub host_dev_name: String, +} + /// Stores the configuration that will be used for loading a snapshot. #[derive(Debug, PartialEq, Eq)] pub struct LoadSnapshotParams { @@ -60,6 +70,8 @@ pub struct LoadSnapshotParams { /// When set to true, the vm is also resumed if the snapshot load /// is successful. pub resume_vm: bool, + /// The network devices to override on load. + pub network_overrides: Vec, } /// Stores the configuration for loading a snapshot that is provided by the user. @@ -82,6 +94,9 @@ pub struct LoadSnapshotConfig { /// Whether or not to resume the vm post snapshot load. #[serde(default)] pub resume_vm: bool, + /// The network devices to override on load. + #[serde(default)] + pub network_overrides: Vec, } /// Stores the configuration used for managing snapshot memory. diff --git a/src/vmm/src/vstate/kvm.rs b/src/vmm/src/vstate/kvm.rs new file mode 100644 index 00000000000..c857aa83080 --- /dev/null +++ b/src/vmm/src/vstate/kvm.rs @@ -0,0 +1,120 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use kvm_bindings::KVM_API_VERSION; +use kvm_ioctls::Kvm as KvmFd; +use serde::{Deserialize, Serialize}; + +pub use crate::arch::{Kvm, KvmArchError}; +use crate::cpu_config::templates::KvmCapability; + +/// Errors associated with the wrappers over KVM ioctls. +/// Needs `rustfmt::skip` to make multiline comments work +#[rustfmt::skip] +#[derive(Debug, thiserror::Error, displaydoc::Display)] +pub enum KvmError { + /// The host kernel reports an invalid KVM API version: {0} + ApiVersion(i32), + /// Missing KVM capabilities: {0:#x?} + Capabilities(u32), + /** Error creating KVM object: {0} Make sure the user launching the firecracker process is \ + configured on the /dev/kvm file's ACL. */ + Kvm(kvm_ioctls::Error), + /// Architecture specific error: {0} + ArchError(#[from] KvmArchError) +} + +impl Kvm { + /// Create `Kvm` struct. + pub fn new(kvm_cap_modifiers: Vec) -> Result { + let kvm_fd = KvmFd::new().map_err(KvmError::Kvm)?; + + // Check that KVM has the correct version. + // Safe to cast because this is a constant. + #[allow(clippy::cast_possible_wrap)] + if kvm_fd.get_api_version() != KVM_API_VERSION as i32 { + return Err(KvmError::ApiVersion(kvm_fd.get_api_version())); + } + + let total_caps = Self::combine_capabilities(&kvm_cap_modifiers); + // Check that all desired capabilities are supported. + Self::check_capabilities(&kvm_fd, &total_caps).map_err(KvmError::Capabilities)?; + + Ok(Kvm::init_arch(kvm_fd, kvm_cap_modifiers)?) + } + + fn combine_capabilities(kvm_cap_modifiers: &[KvmCapability]) -> Vec { + let mut total_caps = Self::DEFAULT_CAPABILITIES.to_vec(); + for modifier in kvm_cap_modifiers.iter() { + match modifier { + KvmCapability::Add(cap) => { + if !total_caps.contains(cap) { + total_caps.push(*cap); + } + } + KvmCapability::Remove(cap) => { + if let Some(pos) = total_caps.iter().position(|c| c == cap) { + total_caps.swap_remove(pos); + } + } + } + } + total_caps + } + + fn check_capabilities(kvm_fd: &KvmFd, capabilities: &[u32]) -> Result<(), u32> { + for cap in capabilities { + // If capability is not supported kernel will return 0. + if kvm_fd.check_extension_raw(u64::from(*cap)) == 0 { + return Err(*cap); + } + } + Ok(()) + } + + /// Saves and returns the Kvm state. + pub fn save_state(&self) -> KvmState { + KvmState { + kvm_cap_modifiers: self.kvm_cap_modifiers.clone(), + } + } + + /// Returns the maximal number of memslots allowed in a [`Vm`] + pub fn max_nr_memslots(&self) -> usize { + self.fd.get_nr_memslots() + } +} + +/// Structure holding an general specific VM state. +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct KvmState { + /// Additional capabilities that were specified in cpu template. + pub kvm_cap_modifiers: Vec, +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + + #[test] + fn test_combine_capabilities() { + // Default caps for x86_64 and aarch64 both have KVM_CAP_IOEVENTFD and don't have + // KVM_CAP_IOMMU caps. + let additional_capabilities = vec![ + KvmCapability::Add(kvm_bindings::KVM_CAP_IOMMU), + KvmCapability::Remove(kvm_bindings::KVM_CAP_IOEVENTFD), + ]; + + let combined_caps = Kvm::combine_capabilities(&additional_capabilities); + assert!( + combined_caps + .iter() + .any(|c| *c == kvm_bindings::KVM_CAP_IOMMU) + ); + assert!( + !combined_caps + .iter() + .any(|c| *c == kvm_bindings::KVM_CAP_IOEVENTFD) + ); + } +} diff --git a/src/vmm/src/vstate/memory.rs b/src/vmm/src/vstate/memory.rs index a84fd6c4be4..19367f7f997 100644 --- a/src/vmm/src/vstate/memory.rs +++ b/src/vmm/src/vstate/memory.rs @@ -7,21 +7,22 @@ use std::fs::File; use std::io::SeekFrom; +use std::sync::Arc; use serde::{Deserialize, Serialize}; -pub use vm_memory::bitmap::{AtomicBitmap, Bitmap, BitmapSlice, BS}; +pub use vm_memory::bitmap::{AtomicBitmap, BS, Bitmap, BitmapSlice}; pub use vm_memory::mmap::MmapRegionBuilder; use vm_memory::mmap::{MmapRegionError, NewBitmap}; pub use vm_memory::{ - address, Address, ByteValued, Bytes, FileOffset, GuestAddress, GuestMemory, GuestMemoryRegion, - GuestUsize, MemoryRegionAddress, MmapRegion, + Address, ByteValued, Bytes, FileOffset, GuestAddress, GuestMemory, GuestMemoryRegion, + GuestUsize, MemoryRegionAddress, MmapRegion, address, }; use vm_memory::{Error as VmMemoryError, GuestMemoryError, WriteVolatile}; use vmm_sys_util::errno; +use crate::DirtyBitmap; use crate::utils::{get_page_size, u64_to_usize}; use crate::vmm_config::machine_config::HugePageConfig; -use crate::DirtyBitmap; /// Type of GuestMemoryMmap. pub type GuestMemoryMmap = vm_memory::GuestMemoryMmap>; @@ -33,12 +34,6 @@ pub type GuestMmapRegion = vm_memory::MmapRegion>; /// Errors associated with dumping guest memory to file. #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum MemoryError { - /// Cannot access file: {0} - FileError(std::io::Error), - /// Cannot create memory: {0} - CreateMemory(VmMemoryError), - /// Cannot create memory region: {0} - CreateRegion(MmapRegionError), /// Cannot fetch system's page size: {0} PageSize(errno::Error), /// Cannot dump memory: {0} @@ -51,8 +46,90 @@ pub enum MemoryError { Memfd(memfd::Error), /// Cannot resize memfd file: {0} MemfdSetLen(std::io::Error), - /// Cannot restore hugetlbfs backed snapshot by mapping the memory file. Please use uffd. - HugetlbfsSnapshot, + /// Total sum of memory regions exceeds largest possible file offset + OffsetTooLarge, +} + +/// Creates a `Vec` of `GuestRegionMmap` with the given configuration +pub fn create( + regions: impl Iterator, + mmap_flags: libc::c_int, + file: Option, + track_dirty_pages: bool, +) -> Result, MemoryError> { + let mut offset = 0; + let file = file.map(Arc::new); + regions + .map(|(start, size)| { + let mut builder = MmapRegionBuilder::new_with_bitmap( + size, + track_dirty_pages.then(|| AtomicBitmap::with_len(size)), + ) + .with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE) + .with_mmap_flags(libc::MAP_NORESERVE | mmap_flags); + + if let Some(ref file) = file { + let file_offset = FileOffset::from_arc(Arc::clone(file), offset); + + builder = builder.with_file_offset(file_offset); + } + + offset = match offset.checked_add(size as u64) { + None => return Err(MemoryError::OffsetTooLarge), + Some(new_off) if new_off >= i64::MAX as u64 => { + return Err(MemoryError::OffsetTooLarge); + } + Some(new_off) => new_off, + }; + + GuestRegionMmap::new( + builder.build().map_err(MemoryError::MmapRegionError)?, + start, + ) + .map_err(MemoryError::VmMemoryError) + }) + .collect::, _>>() +} + +/// Creates a GuestMemoryMmap with `size` in MiB backed by a memfd. +pub fn memfd_backed( + regions: &[(GuestAddress, usize)], + track_dirty_pages: bool, + huge_pages: HugePageConfig, +) -> Result, MemoryError> { + let size = regions.iter().map(|&(_, size)| size as u64).sum(); + let memfd_file = create_memfd(size, huge_pages.into())?.into_file(); + + create( + regions.iter().copied(), + libc::MAP_SHARED | huge_pages.mmap_flags(), + Some(memfd_file), + track_dirty_pages, + ) +} + +/// Creates a GuestMemoryMmap from raw regions. +pub fn anonymous( + regions: impl Iterator, + track_dirty_pages: bool, + huge_pages: HugePageConfig, +) -> Result, MemoryError> { + create( + regions, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | huge_pages.mmap_flags(), + None, + track_dirty_pages, + ) +} + +/// Creates a GuestMemoryMmap given a `file` containing the data +/// and a `state` containing mapping information. +pub fn snapshot_file( + file: File, + regions: impl Iterator, + track_dirty_pages: bool, +) -> Result, MemoryError> { + create(regions, libc::MAP_PRIVATE, Some(file), track_dirty_pages) } /// Defines the interface for snapshotting memory. @@ -60,36 +137,6 @@ pub trait GuestMemoryExtension where Self: Sized, { - /// Creates a GuestMemoryMmap with `size` in MiB backed by a memfd. - fn memfd_backed( - mem_size_mib: usize, - track_dirty_pages: bool, - huge_pages: HugePageConfig, - ) -> Result; - - /// Creates a GuestMemoryMmap from raw regions. - fn from_raw_regions( - regions: &[(GuestAddress, usize)], - track_dirty_pages: bool, - huge_pages: HugePageConfig, - ) -> Result; - - /// Creates a GuestMemoryMmap from raw regions. - fn from_raw_regions_file( - regions: Vec<(FileOffset, GuestAddress, usize)>, - track_dirty_pages: bool, - shared: bool, - ) -> Result; - - /// Creates a GuestMemoryMmap given a `file` containing the data - /// and a `state` containing mapping information. - fn from_state( - file: Option<&File>, - state: &GuestMemoryState, - track_dirty_pages: bool, - huge_pages: HugePageConfig, - ) -> Result; - /// Describes GuestMemoryMmap through a GuestMemoryState struct. fn describe(&self) -> GuestMemoryState; @@ -122,8 +169,6 @@ pub struct GuestMemoryRegionState { pub base_address: u64, /// Region size. pub size: usize, - /// Offset in file/buffer where the region is saved. - pub offset: u64, } /// Describes guest memory regions and their snapshot file mappings. @@ -133,148 +178,25 @@ pub struct GuestMemoryState { pub regions: Vec, } -impl GuestMemoryExtension for GuestMemoryMmap { - /// Creates a GuestMemoryMmap with `size` in MiB backed by a memfd. - fn memfd_backed( - mem_size_mib: usize, - track_dirty_pages: bool, - huge_pages: HugePageConfig, - ) -> Result { - let memfd_file = create_memfd(mem_size_mib, huge_pages.into())?.into_file(); - - let mut offset: u64 = 0; - let regions = crate::arch::arch_memory_regions(mem_size_mib << 20) +impl GuestMemoryState { + /// Turns this [`GuestMemoryState`] into a description of guest memory regions as understood + /// by the creation functions of [`GuestMemoryExtensions`] + pub fn regions(&self) -> impl Iterator + '_ { + self.regions .iter() - .map(|(guest_address, region_size)| { - let file_clone = memfd_file.try_clone().map_err(MemoryError::FileError)?; - let file_offset = FileOffset::new(file_clone, offset); - offset += *region_size as u64; - Ok((file_offset, *guest_address, *region_size)) - }) - .collect::, MemoryError>>()?; - - Self::from_raw_regions_file(regions, track_dirty_pages, true) - } - - /// Creates a GuestMemoryMmap from raw regions backed by anonymous memory. - fn from_raw_regions( - regions: &[(GuestAddress, usize)], - track_dirty_pages: bool, - huge_pages: HugePageConfig, - ) -> Result { - let prot = libc::PROT_READ | libc::PROT_WRITE; - // MAP_NORESERVE for 4K-backed page regions means that no swap space will be reserved for - // the region. For hugetlbfs regions, it means that pages in the hugetlbfs pool will - // not be reserved at mmap-time. This means that instead of failing at mmap-time if - // the hugetlbfs page pool is too small to accommodate the entire VM, Firecracker might - // receive a SIGBUS if a pagefault ever cannot be served due to the pool being depleted. - let flags = - libc::MAP_NORESERVE | libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | huge_pages.mmap_flags(); - - let regions = regions - .iter() - .map(|(guest_address, region_size)| { - let bitmap = match track_dirty_pages { - true => Some(AtomicBitmap::with_len(*region_size)), - false => None, - }; - let region = MmapRegionBuilder::new_with_bitmap(*region_size, bitmap) - .with_mmap_prot(prot) - .with_mmap_flags(flags) - .build() - .map_err(MemoryError::MmapRegionError)?; - - GuestRegionMmap::new(region, *guest_address).map_err(MemoryError::VmMemoryError) - }) - .collect::, MemoryError>>()?; - - GuestMemoryMmap::from_regions(regions).map_err(MemoryError::VmMemoryError) - } - - /// Creates a GuestMemoryMmap from raw regions backed by file. - fn from_raw_regions_file( - regions: Vec<(FileOffset, GuestAddress, usize)>, - track_dirty_pages: bool, - shared: bool, - ) -> Result { - let prot = libc::PROT_READ | libc::PROT_WRITE; - let flags = if shared { - libc::MAP_NORESERVE | libc::MAP_SHARED - } else { - libc::MAP_NORESERVE | libc::MAP_PRIVATE - }; - let regions = regions - .into_iter() - .map(|(file_offset, guest_address, region_size)| { - let bitmap = match track_dirty_pages { - true => Some(AtomicBitmap::with_len(region_size)), - false => None, - }; - let region = MmapRegionBuilder::new_with_bitmap(region_size, bitmap) - .with_mmap_prot(prot) - .with_mmap_flags(flags) - .with_file_offset(file_offset) - .build() - .map_err(MemoryError::MmapRegionError)?; - - GuestRegionMmap::new(region, guest_address).map_err(MemoryError::VmMemoryError) - }) - .collect::, MemoryError>>()?; - - GuestMemoryMmap::from_regions(regions).map_err(MemoryError::VmMemoryError) - } - - /// Creates a GuestMemoryMmap backed by a `file` if present, otherwise backed - /// by anonymous memory. Memory layout and ranges are described in `state` param. - fn from_state( - file: Option<&File>, - state: &GuestMemoryState, - track_dirty_pages: bool, - huge_pages: HugePageConfig, - ) -> Result { - match file { - Some(f) => { - if huge_pages.is_hugetlbfs() { - return Err(MemoryError::HugetlbfsSnapshot); - } - - let regions = state - .regions - .iter() - .map(|r| { - f.try_clone().map(|file_clone| { - let offset = FileOffset::new(file_clone, r.offset); - (offset, GuestAddress(r.base_address), r.size) - }) - }) - .collect::, std::io::Error>>() - .map_err(MemoryError::FileError)?; - - Self::from_raw_regions_file(regions, track_dirty_pages, false) - } - None => { - let regions = state - .regions - .iter() - .map(|r| (GuestAddress(r.base_address), r.size)) - .collect::>(); - Self::from_raw_regions(®ions, track_dirty_pages, huge_pages) - } - } + .map(|region| (GuestAddress(region.base_address), region.size)) } +} +impl GuestMemoryExtension for GuestMemoryMmap { /// Describes GuestMemoryMmap through a GuestMemoryState struct. fn describe(&self) -> GuestMemoryState { let mut guest_memory_state = GuestMemoryState::default(); - let mut offset = 0; self.iter().for_each(|region| { guest_memory_state.regions.push(GuestMemoryRegionState { base_address: region.start_addr().0, size: u64_to_usize(region.len()), - offset, }); - - offset += region.len(); }); guest_memory_state } @@ -305,7 +227,7 @@ impl GuestMemoryExtension for GuestMemoryMmap { let mut writer_offset = 0; let page_size = get_page_size().map_err(MemoryError::PageSize)?; - let write_result = self.iter().enumerate().try_for_each(|(slot, region)| { + let write_result = self.iter().zip(0..).try_for_each(|(region, slot)| { let kvm_bitmap = dirty_bitmap.get(&slot).unwrap(); let firecracker_bitmap = region.bitmap(); let mut write_size = 0; @@ -369,7 +291,7 @@ impl GuestMemoryExtension for GuestMemoryMmap { /// Stores the dirty bitmap inside into the internal bitmap fn store_dirty_bitmap(&self, dirty_bitmap: &DirtyBitmap, page_size: usize) { - self.iter().enumerate().for_each(|(slot, region)| { + self.iter().zip(0..).for_each(|(region, slot)| { let kvm_bitmap = dirty_bitmap.get(&slot).unwrap(); let firecracker_bitmap = region.bitmap(); @@ -389,10 +311,9 @@ impl GuestMemoryExtension for GuestMemoryMmap { } fn create_memfd( - size: usize, + mem_size: u64, hugetlb_size: Option, ) -> Result { - let mem_size = size << 20; // Create a memfd. let opts = memfd::MemfdOptions::default() .hugetlb(hugetlb_size) @@ -402,7 +323,7 @@ fn create_memfd( // Resize to guest mem size. mem_file .as_file() - .set_len(mem_size as u64) + .set_len(mem_size) .map_err(MemoryError::MemfdSetLen)?; // Add seals to prevent further resizing. @@ -430,29 +351,11 @@ mod tests { use super::*; use crate::snapshot::Snapshot; - use crate::utils::get_page_size; + use crate::utils::{get_page_size, mib_to_bytes}; #[test] - fn test_from_raw_regions() { - // Check dirty page tracking is off. - { - let region_size = 0x10000; - let regions = vec![ - (GuestAddress(0x0), region_size), - (GuestAddress(0x10000), region_size), - (GuestAddress(0x20000), region_size), - (GuestAddress(0x30000), region_size), - ]; - - let guest_memory = - GuestMemoryMmap::from_raw_regions(®ions, false, HugePageConfig::None).unwrap(); - guest_memory.iter().for_each(|region| { - assert!(region.bitmap().is_none()); - }); - } - - // Check dirty page tracking is on. - { + fn test_anonymous() { + for dirty_page_tracking in [true, false] { let region_size = 0x10000; let regions = vec![ (GuestAddress(0x0), region_size), @@ -461,94 +364,18 @@ mod tests { (GuestAddress(0x30000), region_size), ]; - let guest_memory = - GuestMemoryMmap::from_raw_regions(®ions, true, HugePageConfig::None).unwrap(); - guest_memory.iter().for_each(|region| { - assert!(region.bitmap().is_some()); - }); - } - } - - #[test] - fn test_from_raw_regions_file() { - let region_size = 0x10000; - - let file = TempFile::new().unwrap().into_file(); - let file_size = 4 * region_size; - file.set_len(file_size as u64).unwrap(); - - let regions = vec![ - ( - FileOffset::new(file.try_clone().unwrap(), 0x0), - GuestAddress(0x0), - region_size, - ), - ( - FileOffset::new(file.try_clone().unwrap(), 0x10000), - GuestAddress(0x10000), - region_size, - ), - ( - FileOffset::new(file.try_clone().unwrap(), 0x20000), - GuestAddress(0x20000), - region_size, - ), - ( - FileOffset::new(file.try_clone().unwrap(), 0x30000), - GuestAddress(0x30000), - region_size, - ), - ]; - - // Test that all regions are guarded. - { - let guest_memory = - GuestMemoryMmap::from_raw_regions_file(regions.clone(), false, false).unwrap(); - guest_memory.iter().for_each(|region| { - assert_eq!(region.size(), region_size); - assert!(region.file_offset().is_some()); - assert!(region.bitmap().is_none()); - }); - } - - // Check dirty page tracking is off. - { - let guest_memory = - GuestMemoryMmap::from_raw_regions_file(regions.clone(), false, false).unwrap(); - guest_memory.iter().for_each(|region| { - assert!(region.bitmap().is_none()); - }); - } - - // Check dirty page tracking is on. - { - let guest_memory = - GuestMemoryMmap::from_raw_regions_file(regions, true, false).unwrap(); + let guest_memory = anonymous( + regions.into_iter(), + dirty_page_tracking, + HugePageConfig::None, + ) + .unwrap(); guest_memory.iter().for_each(|region| { - assert!(region.bitmap().is_some()); + assert_eq!(region.bitmap().is_some(), dirty_page_tracking); }); } } - #[test] - fn test_from_state() { - let state = GuestMemoryState { - regions: vec![GuestMemoryRegionState { - base_address: 0, - size: 4096, - offset: 0, - }], - }; - let file = TempFile::new().unwrap().into_file(); - - // No mapping of snapshots that were taken with hugetlbfs enabled - let err = - GuestMemoryMmap::from_state(Some(&file), &state, false, HugePageConfig::Hugetlbfs2M) - .unwrap_err(); - - assert!(matches!(err, MemoryError::HugetlbfsSnapshot), "{:?}", err); - } - #[test] fn test_mark_dirty() { let page_size = get_page_size().unwrap(); @@ -559,8 +386,10 @@ mod tests { (GuestAddress(region_size as u64), region_size), // pages 3-5 (GuestAddress(region_size as u64 * 2), region_size), // pages 6-8 ]; - let guest_memory = - GuestMemoryMmap::from_raw_regions(®ions, true, HugePageConfig::None).unwrap(); + let guest_memory = GuestMemoryMmap::from_regions( + anonymous(regions.into_iter(), true, HugePageConfig::None).unwrap(), + ) + .unwrap(); let dirty_map = [ // page 0: not dirty @@ -615,10 +444,13 @@ mod tests { let region_size = page_size * 3; // Test with a single region - let guest_memory = GuestMemoryMmap::from_raw_regions( - &[(GuestAddress(0), region_size)], - false, - HugePageConfig::None, + let guest_memory = GuestMemoryMmap::from_regions( + anonymous( + [(GuestAddress(0), region_size)].into_iter(), + false, + HugePageConfig::None, + ) + .unwrap(), ) .unwrap(); check_serde(&guest_memory); @@ -629,8 +461,10 @@ mod tests { (GuestAddress(region_size as u64), region_size), // pages 3-5 (GuestAddress(region_size as u64 * 2), region_size), // pages 6-8 ]; - let guest_memory = - GuestMemoryMmap::from_raw_regions(®ions, true, HugePageConfig::None).unwrap(); + let guest_memory = GuestMemoryMmap::from_regions( + anonymous(regions.into_iter(), true, HugePageConfig::None).unwrap(), + ) + .unwrap(); check_serde(&guest_memory); } @@ -643,21 +477,20 @@ mod tests { (GuestAddress(0), page_size), (GuestAddress(page_size as u64 * 2), page_size), ]; - let guest_memory = - GuestMemoryMmap::from_raw_regions(&mem_regions[..], true, HugePageConfig::None) - .unwrap(); + let guest_memory = GuestMemoryMmap::from_regions( + anonymous(mem_regions.into_iter(), true, HugePageConfig::None).unwrap(), + ) + .unwrap(); let expected_memory_state = GuestMemoryState { regions: vec![ GuestMemoryRegionState { base_address: 0, size: page_size, - offset: 0, }, GuestMemoryRegionState { base_address: page_size as u64 * 2, size: page_size, - offset: page_size as u64, }, ], }; @@ -670,21 +503,20 @@ mod tests { (GuestAddress(0), page_size * 3), (GuestAddress(page_size as u64 * 4), page_size * 3), ]; - let guest_memory = - GuestMemoryMmap::from_raw_regions(&mem_regions[..], true, HugePageConfig::None) - .unwrap(); + let guest_memory = GuestMemoryMmap::from_regions( + anonymous(mem_regions.into_iter(), true, HugePageConfig::None).unwrap(), + ) + .unwrap(); let expected_memory_state = GuestMemoryState { regions: vec![ GuestMemoryRegionState { base_address: 0, size: page_size * 3, - offset: 0, }, GuestMemoryRegionState { base_address: page_size as u64 * 4, size: page_size * 3, - offset: page_size as u64 * 3, }, ], }; @@ -705,8 +537,10 @@ mod tests { (region_1_address, region_size), (region_2_address, region_size), ]; - let guest_memory = - GuestMemoryMmap::from_raw_regions(&mem_regions, true, HugePageConfig::None).unwrap(); + let guest_memory = GuestMemoryMmap::from_regions( + anonymous(mem_regions.into_iter(), true, HugePageConfig::None).unwrap(), + ) + .unwrap(); // Check that Firecracker bitmap is clean. guest_memory.iter().for_each(|r| { assert!(!r.bitmap().dirty_at(0)); @@ -728,11 +562,8 @@ mod tests { let mut memory_file = TempFile::new().unwrap().into_file(); guest_memory.dump(&mut memory_file).unwrap(); - let restored_guest_memory = GuestMemoryMmap::from_state( - Some(&memory_file), - &memory_state, - false, - HugePageConfig::None, + let restored_guest_memory = GuestMemoryMmap::from_regions( + snapshot_file(memory_file, memory_state.regions(), false).unwrap(), ) .unwrap(); @@ -761,8 +592,10 @@ mod tests { (region_1_address, region_size), (region_2_address, region_size), ]; - let guest_memory = - GuestMemoryMmap::from_raw_regions(&mem_regions, true, HugePageConfig::None).unwrap(); + let guest_memory = GuestMemoryMmap::from_regions( + anonymous(mem_regions.into_iter(), true, HugePageConfig::None).unwrap(), + ) + .unwrap(); // Check that Firecracker bitmap is clean. guest_memory.iter().for_each(|r| { assert!(!r.bitmap().dirty_at(0)); @@ -791,9 +624,10 @@ mod tests { guest_memory.dump_dirty(&mut file, &dirty_bitmap).unwrap(); // We can restore from this because this is the first dirty dump. - let restored_guest_memory = - GuestMemoryMmap::from_state(Some(&file), &memory_state, false, HugePageConfig::None) - .unwrap(); + let restored_guest_memory = GuestMemoryMmap::from_regions( + snapshot_file(file, memory_state.regions(), false).unwrap(), + ) + .unwrap(); // Check that the region contents are the same. let mut restored_region = vec![0u8; region_size]; @@ -849,8 +683,10 @@ mod tests { (region_1_address, region_size), (region_2_address, region_size), ]; - let guest_memory = - GuestMemoryMmap::from_raw_regions(&mem_regions, true, HugePageConfig::None).unwrap(); + let guest_memory = GuestMemoryMmap::from_regions( + anonymous(mem_regions.into_iter(), true, HugePageConfig::None).unwrap(), + ) + .unwrap(); // Check that Firecracker bitmap is clean. guest_memory.iter().for_each(|r| { @@ -875,12 +711,11 @@ mod tests { #[test] fn test_create_memfd() { - let size = 1; - let size_mb = 1 << 20; + let size_bytes = mib_to_bytes(1) as u64; - let memfd = create_memfd(size, None).unwrap(); + let memfd = create_memfd(size_bytes, None).unwrap(); - assert_eq!(memfd.as_file().metadata().unwrap().len(), size_mb); + assert_eq!(memfd.as_file().metadata().unwrap().len(), size_bytes); memfd.as_file().set_len(0x69).unwrap_err(); let mut seals = memfd::SealsHashSet::new(); diff --git a/src/vmm/src/vstate/mod.rs b/src/vmm/src/vstate/mod.rs index 32d7bd7ea7f..47458835e04 100644 --- a/src/vmm/src/vstate/mod.rs +++ b/src/vmm/src/vstate/mod.rs @@ -1,6 +1,8 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +/// Module with Kvm implementation. +pub mod kvm; /// Module with GuestMemory implementation. pub mod memory; /// Module with Vcpu implementation. diff --git a/src/vmm/src/vstate/vcpu/mod.rs b/src/vmm/src/vstate/vcpu.rs similarity index 93% rename from src/vmm/src/vstate/vcpu/mod.rs rename to src/vmm/src/vstate/vcpu.rs index cb63afa4579..825af33eea4 100644 --- a/src/vmm/src/vstate/vcpu/mod.rs +++ b/src/vmm/src/vstate/vcpu.rs @@ -8,8 +8,8 @@ use std::cell::Cell; #[cfg(feature = "gdb")] use std::os::fd::AsRawFd; -use std::sync::atomic::{fence, Ordering}; -use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; +use std::sync::atomic::{Ordering, fence}; +use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel}; use std::sync::{Arc, Barrier}; use std::{fmt, io, thread}; @@ -19,30 +19,19 @@ use kvm_ioctls::VcpuExit; use kvm_ioctls::VcpuFd; use libc::{c_int, c_void, siginfo_t}; use log::{error, info, warn}; -use seccompiler::{BpfProgram, BpfProgramRef}; use vmm_sys_util::errno; use vmm_sys_util::eventfd::EventFd; +use crate::FcExitCode; +pub use crate::arch::{KvmVcpu, KvmVcpuConfigureError, KvmVcpuError, Peripherals, VcpuState}; use crate::cpu_config::templates::{CpuConfiguration, GuestConfigError}; #[cfg(feature = "gdb")] -use crate::gdb::target::{get_raw_tid, GdbTargetError}; +use crate::gdb::target::{GdbTargetError, get_raw_tid}; use crate::logger::{IncMetric, METRICS}; -use crate::utils::signal::{register_signal_handler, sigrtmin, Killable}; +use crate::seccomp::{BpfProgram, BpfProgramRef}; +use crate::utils::signal::{Killable, register_signal_handler, sigrtmin}; use crate::utils::sm::StateMachine; use crate::vstate::vm::Vm; -use crate::FcExitCode; - -/// Module with aarch64 vCPU implementation. -#[cfg(target_arch = "aarch64")] -pub mod aarch64; -/// Module with x86_64 vCPU implementation. -#[cfg(target_arch = "x86_64")] -pub mod x86_64; - -#[cfg(target_arch = "aarch64")] -pub use aarch64::{KvmVcpuError, *}; -#[cfg(target_arch = "x86_64")] -pub use x86_64::{KvmVcpuError, *}; /// Signal number (SIGRTMIN) used to kick Vcpus. pub const VCPU_RTSIG_OFFSET: i32 = 0; @@ -176,9 +165,9 @@ impl Vcpu { { Self::TLS_VCPU_PTR.with(|cell: &VcpuCell| { if let Some(vcpu_ptr) = cell.get() { - // Dereferencing here is safe since `TLS_VCPU_PTR` is populated/non-empty, + // SAFETY: Dereferencing here is safe since `TLS_VCPU_PTR` is populated/non-empty, // and it is being cleared on `Vcpu::drop` so there is no dangling pointer. - let vcpu_ref = &mut *vcpu_ptr; + let vcpu_ref = unsafe { &mut *vcpu_ptr }; func(vcpu_ref); Ok(()) } else { @@ -288,7 +277,7 @@ impl Vcpu { // Load seccomp filters for this vCPU thread. // Execution panics if filters cannot be loaded, use --no-seccomp if skipping filters // altogether is the desired behaviour. - if let Err(err) = seccompiler::apply_filter(seccomp_filter) { + if let Err(err) = crate::seccomp::apply_filter(seccomp_filter) { panic!( "Failed to set the requested seccomp filters on vCPU {}: Error: {}", self.kvm_vcpu.index, err @@ -317,6 +306,16 @@ impl Vcpu { // If the emulation requests a pause lets do this #[cfg(feature = "gdb")] Ok(VcpuEmulation::Paused) => { + // Calling `KVM_KVMCLOCK_CTRL` to make sure the guest softlockup watchdog + // does not panic on resume, see https://docs.kernel.org/virt/kvm/api.html . + // We do not want to fail if the call is not successful, because depending + // that may be acceptable depending on the workload. + #[cfg(target_arch = "x86_64")] + if let Err(err) = self.kvm_vcpu.fd.kvmclock_ctrl() { + METRICS.vcpu.kvmclock_ctrl_fails.inc(); + warn!("KVM_KVMCLOCK_CTRL call failed {}", err); + } + return StateMachine::next(Self::paused); } // Emulation errors lead to vCPU exit. @@ -665,8 +664,8 @@ impl fmt::Debug for VcpuResponse { Resumed => write!(f, "VcpuResponse::Resumed"), Exited(code) => write!(f, "VcpuResponse::Exited({:?})", code), SavedState(_) => write!(f, "VcpuResponse::SavedState"), - Error(ref err) => write!(f, "VcpuResponse::Error({:?})", err), - NotAllowed(ref reason) => write!(f, "VcpuResponse::NotAllowed({})", reason), + Error(err) => write!(f, "VcpuResponse::Error({:?})", err), + NotAllowed(reason) => write!(f, "VcpuResponse::NotAllowed({})", reason), DumpedCpuConfig(_) => write!(f, "VcpuResponse::DumpedCpuConfig"), } } @@ -759,7 +758,7 @@ pub enum VcpuEmulation { } #[cfg(test)] -pub mod tests { +pub(crate) mod tests { #![allow(clippy::undocumented_unsafe_blocks)] #[cfg(target_arch = "x86_64")] @@ -770,20 +769,22 @@ pub mod tests { use vmm_sys_util::errno; use super::*; - use crate::builder::StartMicrovmError; - use crate::devices::bus::DummyDevice; + use crate::RECV_TIMEOUT_SEC; + use crate::arch::{BootProtocol, EntryPoint}; use crate::devices::BusDevice; - use crate::seccomp_filters::get_empty_filters; + use crate::devices::bus::DummyDevice; + use crate::seccomp::get_empty_filters; + use crate::utils::mib_to_bytes; use crate::utils::signal::validate_signal_num; + use crate::vstate::kvm::Kvm; use crate::vstate::memory::{GuestAddress, GuestMemoryMmap}; use crate::vstate::vcpu::VcpuError as EmulationError; - use crate::vstate::vm::tests::setup_vm; use crate::vstate::vm::Vm; - use crate::RECV_TIMEOUT_SEC; + use crate::vstate::vm::tests::setup_vm_with_memory; #[test] fn test_handle_kvm_exit() { - let (_vm, mut vcpu, _vm_mem) = setup_vcpu(0x1000); + let (_, _, mut vcpu) = setup_vcpu(0x1000); let res = handle_kvm_exit(&mut vcpu.kvm_vcpu.peripherals, Ok(VcpuExit::Hlt)); assert_eq!(res.unwrap(), VcpuEmulation::Stopped); @@ -908,7 +909,7 @@ pub mod tests { (NotAllowed(_), NotAllowed(_)) | (SavedState(_), SavedState(_)) | (DumpedCpuConfig(_), DumpedCpuConfig(_)) => true, - (Error(ref err), Error(ref other_err)) => { + (Error(err), Error(other_err)) => { format!("{:?}", err) == format!("{:?}", other_err) } _ => false, @@ -918,24 +919,16 @@ pub mod tests { // Auxiliary function being used throughout the tests. #[allow(unused_mut)] - pub(crate) fn setup_vcpu(mem_size: usize) -> (Vm, Vcpu, GuestMemoryMmap) { - let (mut vm, gm) = setup_vm(mem_size); + pub(crate) fn setup_vcpu(mem_size: usize) -> (Kvm, Vm, Vcpu) { + let (kvm, mut vm) = setup_vm_with_memory(mem_size); - let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let (mut vcpus, _) = vm.create_vcpus(1).unwrap(); + let mut vcpu = vcpus.remove(0); #[cfg(target_arch = "aarch64")] - let vcpu = { - let mut vcpu = Vcpu::new(1, &vm, exit_evt).unwrap(); - vcpu.kvm_vcpu.init(&[]).unwrap(); - vm.setup_irqchip(1).unwrap(); - vcpu - }; - #[cfg(target_arch = "x86_64")] - let vcpu = { - vm.setup_irqchip().unwrap(); - Vcpu::new(1, &vm, exit_evt).unwrap() - }; - (vm, vcpu, gm) + vcpu.kvm_vcpu.init(&[]).unwrap(); + + (kvm, vm, vcpu) } fn load_good_kernel(vm_memory: &GuestMemoryMmap) -> GuestAddress { @@ -958,37 +951,39 @@ pub mod tests { &mut kernel_file, Some(GuestAddress(crate::arch::get_kernel_start())), ) - .map_err(StartMicrovmError::KernelLoader); + .unwrap(); #[cfg(target_arch = "aarch64")] let entry_addr = - linux_loader::loader::pe::PE::load(vm_memory, None, &mut kernel_file, None) - .map_err(StartMicrovmError::KernelLoader); - entry_addr.unwrap().kernel_load + linux_loader::loader::pe::PE::load(vm_memory, None, &mut kernel_file, None).unwrap(); + entry_addr.kernel_load } - fn vcpu_configured_for_boot() -> (VcpuHandle, vmm_sys_util::eventfd::EventFd) { + fn vcpu_configured_for_boot() -> (Vm, VcpuHandle, EventFd) { Vcpu::register_kick_signal_handler(); // Need enough mem to boot linux. - let mem_size = 64 << 20; - let (_vm, mut vcpu, vm_mem) = setup_vcpu(mem_size); + let mem_size = mib_to_bytes(64); + let (kvm, vm, mut vcpu) = setup_vcpu(mem_size); let vcpu_exit_evt = vcpu.exit_evt.try_clone().unwrap(); // Needs a kernel since we'll actually run this vcpu. - let entry_addr = load_good_kernel(&vm_mem); + let entry_point = EntryPoint { + entry_addr: load_good_kernel(vm.guest_memory()), + protocol: BootProtocol::LinuxBoot, + }; #[cfg(target_arch = "x86_64")] { use crate::cpu_config::x86_64::cpuid::Cpuid; vcpu.kvm_vcpu .configure( - &vm_mem, - entry_addr, + vm.guest_memory(), + entry_point, &VcpuConfig { vcpu_count: 1, smt: false, cpu_config: CpuConfiguration { - cpuid: Cpuid::try_from(_vm.supported_cpuid().clone()).unwrap(), + cpuid: Cpuid::try_from(kvm.supported_cpuid.clone()).unwrap(), msrs: BTreeMap::new(), }, }, @@ -999,13 +994,14 @@ pub mod tests { #[cfg(target_arch = "aarch64")] vcpu.kvm_vcpu .configure( - &vm_mem, - entry_addr, + vm.guest_memory(), + entry_point, &VcpuConfig { vcpu_count: 1, smt: false, cpu_config: crate::cpu_config::aarch64::CpuConfiguration::default(), }, + &kvm.optional_capabilities(), ) .expect("failed to configure vcpu"); @@ -1017,12 +1013,12 @@ pub mod tests { // Wait for vCPUs to initialize their TLS before moving forward. barrier.wait(); - (vcpu_handle, vcpu_exit_evt) + (vm, vcpu_handle, vcpu_exit_evt) } #[test] fn test_set_mmio_bus() { - let (_, mut vcpu, _) = setup_vcpu(0x1000); + let (_, _, mut vcpu) = setup_vcpu(0x1000); assert!(vcpu.kvm_vcpu.peripherals.mmio_bus.is_none()); vcpu.set_mmio_bus(crate::devices::Bus::new()); assert!(vcpu.kvm_vcpu.peripherals.mmio_bus.is_some()); @@ -1030,7 +1026,7 @@ pub mod tests { #[test] fn test_vcpu_tls() { - let (_, mut vcpu, _) = setup_vcpu(0x1000); + let (_, _, mut vcpu) = setup_vcpu(0x1000); // Running on the TLS vcpu should fail before we actually initialize it. unsafe { @@ -1061,7 +1057,7 @@ pub mod tests { #[test] fn test_invalid_tls() { - let (_, mut vcpu, _) = setup_vcpu(0x1000); + let (_, _, mut vcpu) = setup_vcpu(0x1000); // Initialize vcpu TLS. vcpu.init_thread_local_data().unwrap(); // Trying to initialize non-empty TLS should error. @@ -1071,7 +1067,7 @@ pub mod tests { #[test] fn test_vcpu_kick() { Vcpu::register_kick_signal_handler(); - let (vm, mut vcpu, _) = setup_vcpu(0x1000); + let (_, vm, mut vcpu) = setup_vcpu(0x1000); let mut kvm_run = kvm_ioctls::KvmRunWrapper::mmap_from_fd(&vcpu.kvm_vcpu.fd, vm.fd().run_size()) @@ -1126,7 +1122,7 @@ pub mod tests { #[test] fn test_immediate_exit_shortcircuits_execution() { - let (_vm, mut vcpu, _) = setup_vcpu(0x1000); + let (_, _, mut vcpu) = setup_vcpu(0x1000); vcpu.kvm_vcpu.fd.set_kvm_immediate_exit(1); // Set a dummy value to be returned by the emulate call @@ -1151,7 +1147,7 @@ pub mod tests { #[test] fn test_vcpu_pause_resume() { - let (vcpu_handle, vcpu_exit_evt) = vcpu_configured_for_boot(); + let (_vm, vcpu_handle, vcpu_exit_evt) = vcpu_configured_for_boot(); // Queue a Resume event, expect a response. queue_event_expect_response(&vcpu_handle, VcpuEvent::Resume, VcpuResponse::Resumed); @@ -1183,7 +1179,7 @@ pub mod tests { #[test] fn test_vcpu_save_state_events() { - let (vcpu_handle, _vcpu_exit_evt) = vcpu_configured_for_boot(); + let (_vm, vcpu_handle, _vcpu_exit_evt) = vcpu_configured_for_boot(); // Queue a Resume event, expect a response. queue_event_expect_response(&vcpu_handle, VcpuEvent::Resume, VcpuResponse::Resumed); @@ -1216,7 +1212,7 @@ pub mod tests { #[test] fn test_vcpu_dump_cpu_config() { - let (vcpu_handle, _) = vcpu_configured_for_boot(); + let (_vm, vcpu_handle, _) = vcpu_configured_for_boot(); // Queue a DumpCpuConfig event, expect a DumpedCpuConfig response. vcpu_handle diff --git a/src/vmm/src/vstate/vcpu/aarch64.rs b/src/vmm/src/vstate/vcpu/aarch64.rs deleted file mode 100644 index 2e2bb36fb07..00000000000 --- a/src/vmm/src/vstate/vcpu/aarch64.rs +++ /dev/null @@ -1,475 +0,0 @@ -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -// -// 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 THIRD-PARTY file. - -use std::fmt::{Debug, Write}; - -use kvm_bindings::{ - kvm_mp_state, kvm_vcpu_init, KVM_ARM_VCPU_POWER_OFF, KVM_ARM_VCPU_PSCI_0_2, KVM_ARM_VCPU_SVE, -}; -use kvm_ioctls::*; -use serde::{Deserialize, Serialize}; - -use crate::arch::aarch64::regs::{Aarch64RegisterVec, KVM_REG_ARM64_SVE_VLS}; -use crate::arch::aarch64::vcpu::{ - get_all_registers, get_all_registers_ids, get_mpidr, get_mpstate, get_registers, set_mpstate, - set_register, setup_boot_regs, VcpuError as ArchError, -}; -use crate::cpu_config::aarch64::custom_cpu_template::VcpuFeatures; -use crate::cpu_config::templates::CpuConfiguration; -use crate::logger::{error, IncMetric, METRICS}; -use crate::vcpu::{VcpuConfig, VcpuError}; -use crate::vstate::memory::{Address, GuestAddress, GuestMemoryMmap}; -use crate::vstate::vcpu::VcpuEmulation; -use crate::vstate::vm::Vm; - -/// Errors associated with the wrappers over KVM ioctls. -#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] -pub enum KvmVcpuError { - /// Error configuring the vcpu registers: {0} - ConfigureRegisters(ArchError), - /// Error creating vcpu: {0} - CreateVcpu(kvm_ioctls::Error), - /// Failed to dump CPU configuration: {0} - DumpCpuConfig(ArchError), - /// Error getting the vcpu preferred target: {0} - GetPreferredTarget(kvm_ioctls::Error), - /// Error initializing the vcpu: {0} - Init(kvm_ioctls::Error), - /// Error applying template: {0} - ApplyCpuTemplate(ArchError), - /// Failed to restore the state of the vcpu: {0} - RestoreState(ArchError), - /// Failed to save the state of the vcpu: {0} - SaveState(ArchError), -} - -/// Error type for [`KvmVcpu::configure`]. -pub type KvmVcpuConfigureError = KvmVcpuError; - -/// A wrapper around creating and using a kvm aarch64 vcpu. -#[derive(Debug)] -pub struct KvmVcpu { - /// Index of vcpu. - pub index: u8, - /// KVM vcpu fd. - pub fd: VcpuFd, - /// Vcpu peripherals, such as buses - pub(super) peripherals: Peripherals, - mpidr: u64, - kvi: kvm_vcpu_init, -} - -/// Vcpu peripherals -#[derive(Default, Debug)] -pub(super) struct Peripherals { - /// mmio bus. - pub mmio_bus: Option, -} - -impl KvmVcpu { - /// Constructs a new kvm vcpu with arch specific functionality. - /// - /// # Arguments - /// - /// * `index` - Represents the 0-based CPU index between [0, max vcpus). - /// * `vm` - The vm to which this vcpu will get attached. - pub fn new(index: u8, vm: &Vm) -> Result { - let kvm_vcpu = vm - .fd() - .create_vcpu(index.into()) - .map_err(KvmVcpuError::CreateVcpu)?; - - let mut kvi = Self::default_kvi(vm.fd())?; - // Secondary vcpus must be powered off for boot process. - if 0 < index { - kvi.features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF; - } - - Ok(KvmVcpu { - index, - fd: kvm_vcpu, - peripherals: Default::default(), - mpidr: 0, - kvi, - }) - } - - /// Gets the MPIDR register value. - pub fn get_mpidr(&self) -> u64 { - self.mpidr - } - - /// Configures an aarch64 specific vcpu for booting Linux. - /// - /// # Arguments - /// - /// * `guest_mem` - The guest memory used by this microvm. - /// * `kernel_load_addr` - Offset from `guest_mem` at which the kernel is loaded. - /// * `vcpu_config` - The vCPU configuration. - pub fn configure( - &mut self, - guest_mem: &GuestMemoryMmap, - kernel_load_addr: GuestAddress, - vcpu_config: &VcpuConfig, - ) -> Result<(), KvmVcpuError> { - for reg in vcpu_config.cpu_config.regs.iter() { - self.fd - .set_one_reg(reg.id, reg.as_slice()) - .map_err(|err| KvmVcpuError::ApplyCpuTemplate(ArchError::SetOneReg(reg.id, err)))?; - } - - setup_boot_regs( - &self.fd, - self.index, - kernel_load_addr.raw_value(), - guest_mem, - ) - .map_err(KvmVcpuError::ConfigureRegisters)?; - - self.mpidr = get_mpidr(&self.fd).map_err(KvmVcpuError::ConfigureRegisters)?; - - Ok(()) - } - - /// Initializes an aarch64 specific vcpu for booting Linux. - /// - /// # Arguments - /// - /// * `vm_fd` - The kvm `VmFd` for this microvm. - pub fn init(&mut self, vcpu_features: &[VcpuFeatures]) -> Result<(), KvmVcpuError> { - for feature in vcpu_features.iter() { - let index = feature.index as usize; - self.kvi.features[index] = feature.bitmap.apply(self.kvi.features[index]); - } - - self.init_vcpu()?; - self.finalize_vcpu()?; - - Ok(()) - } - - /// Creates default kvi struct based on vcpu index. - pub fn default_kvi(vm_fd: &VmFd) -> Result { - let mut kvi = kvm_vcpu_init::default(); - // This reads back the kernel's preferred target type. - vm_fd - .get_preferred_target(&mut kvi) - .map_err(KvmVcpuError::GetPreferredTarget)?; - // We already checked that the capability is supported. - kvi.features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; - - Ok(kvi) - } - - /// Save the KVM internal state. - pub fn save_state(&self) -> Result { - let mut state = VcpuState { - mp_state: get_mpstate(&self.fd).map_err(KvmVcpuError::SaveState)?, - ..Default::default() - }; - get_all_registers(&self.fd, &mut state.regs).map_err(KvmVcpuError::SaveState)?; - state.mpidr = get_mpidr(&self.fd).map_err(KvmVcpuError::SaveState)?; - - state.kvi = self.kvi; - // We don't save power off state in a snapshot, because - // it was only needed during uVM boot process. - // When uVM is restored, the kernel has already passed - // the boot state and turned secondary vcpus on. - state.kvi.features[0] &= !(1 << KVM_ARM_VCPU_POWER_OFF); - - Ok(state) - } - - /// Use provided state to populate KVM internal state. - pub fn restore_state(&mut self, state: &VcpuState) -> Result<(), KvmVcpuError> { - self.kvi = state.kvi; - - self.init_vcpu()?; - - // If KVM_REG_ARM64_SVE_VLS is present it needs to - // be set before vcpu is finalized. - if let Some(sve_vls_reg) = state - .regs - .iter() - .find(|reg| reg.id == KVM_REG_ARM64_SVE_VLS) - { - set_register(&self.fd, sve_vls_reg).map_err(KvmVcpuError::RestoreState)?; - } - - self.finalize_vcpu()?; - - // KVM_REG_ARM64_SVE_VLS needs to be skipped after vcpu is finalized. - // If it is present it is handled in the code above. - for reg in state - .regs - .iter() - .filter(|reg| reg.id != KVM_REG_ARM64_SVE_VLS) - { - set_register(&self.fd, reg).map_err(KvmVcpuError::RestoreState)?; - } - set_mpstate(&self.fd, state.mp_state).map_err(KvmVcpuError::RestoreState)?; - Ok(()) - } - - /// Dumps CPU configuration. - pub fn dump_cpu_config(&self) -> Result { - let reg_list = get_all_registers_ids(&self.fd).map_err(KvmVcpuError::DumpCpuConfig)?; - - let mut regs = Aarch64RegisterVec::default(); - get_registers(&self.fd, ®_list, &mut regs).map_err(KvmVcpuError::DumpCpuConfig)?; - - Ok(CpuConfiguration { regs }) - } - /// Initializes internal vcpufd. - fn init_vcpu(&self) -> Result<(), KvmVcpuError> { - self.fd.vcpu_init(&self.kvi).map_err(KvmVcpuError::Init)?; - Ok(()) - } - - /// Checks for SVE feature and calls `vcpu_finalize` if - /// it is enabled. - fn finalize_vcpu(&self) -> Result<(), KvmVcpuError> { - if (self.kvi.features[0] & (1 << KVM_ARM_VCPU_SVE)) != 0 { - // KVM_ARM_VCPU_SVE has value 4 so casting to i32 is safe. - #[allow(clippy::cast_possible_wrap)] - let feature = KVM_ARM_VCPU_SVE as i32; - self.fd.vcpu_finalize(&feature).unwrap(); - } - Ok(()) - } -} - -impl Peripherals { - /// Runs the vCPU in KVM context and handles the kvm exit reason. - /// - /// Returns error or enum specifying whether emulation was handled or interrupted. - pub fn run_arch_emulation(&self, exit: VcpuExit) -> Result { - METRICS.vcpu.failures.inc(); - // TODO: Are we sure we want to finish running a vcpu upon - // receiving a vm exit that is not necessarily an error? - error!("Unexpected exit reason on vcpu run: {:?}", exit); - Err(VcpuError::UnhandledKvmExit(format!("{:?}", exit))) - } -} - -/// Structure holding VCPU kvm state. -#[derive(Default, Clone, Serialize, Deserialize)] -pub struct VcpuState { - /// Multiprocessing state. - pub mp_state: kvm_mp_state, - /// Vcpu registers. - pub regs: Aarch64RegisterVec, - /// We will be using the mpidr for passing it to the VmState. - /// The VmState will give this away for saving restoring the icc and redistributor - /// registers. - pub mpidr: u64, - /// kvi states for vcpu initialization. - pub kvi: kvm_vcpu_init, -} - -impl Debug for VcpuState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "kvm_mp_state: {:#x}", self.mp_state.mp_state)?; - writeln!(f, "mpidr: {:#x}", self.mpidr)?; - for reg in self.regs.iter() { - writeln!( - f, - "{:#x} 0x{}", - reg.id, - reg.as_slice() - .iter() - .rev() - .fold(String::new(), |mut output, b| { - let _ = write!(output, "{b:x}"); - output - }) - )?; - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - #![allow(clippy::undocumented_unsafe_blocks)] - use std::os::unix::io::AsRawFd; - - use kvm_bindings::{KVM_ARM_VCPU_PSCI_0_2, KVM_REG_SIZE_U64}; - - use super::*; - use crate::arch::aarch64::regs::Aarch64RegisterRef; - use crate::cpu_config::aarch64::CpuConfiguration; - use crate::cpu_config::templates::RegisterValueFilter; - use crate::vcpu::VcpuConfig; - use crate::vstate::memory::GuestMemoryMmap; - use crate::vstate::vm::tests::setup_vm; - use crate::vstate::vm::Vm; - - fn setup_vcpu(mem_size: usize) -> (Vm, KvmVcpu, GuestMemoryMmap) { - let (mut vm, vm_mem) = setup_vm(mem_size); - let mut vcpu = KvmVcpu::new(0, &vm).unwrap(); - vcpu.init(&[]).unwrap(); - vm.setup_irqchip(1).unwrap(); - - (vm, vcpu, vm_mem) - } - - #[test] - fn test_create_vcpu() { - let (vm, _) = setup_vm(0x1000); - - unsafe { libc::close(vm.fd().as_raw_fd()) }; - - let err = KvmVcpu::new(0, &vm); - assert_eq!( - err.err().unwrap().to_string(), - "Error creating vcpu: Bad file descriptor (os error 9)".to_string() - ); - } - - #[test] - fn test_configure_vcpu() { - let (_vm, mut vcpu, vm_mem) = setup_vcpu(0x10000); - - let vcpu_config = VcpuConfig { - vcpu_count: 1, - smt: false, - cpu_config: CpuConfiguration::default(), - }; - vcpu.configure( - &vm_mem, - GuestAddress(crate::arch::get_kernel_start()), - &vcpu_config, - ) - .unwrap(); - - unsafe { libc::close(vcpu.fd.as_raw_fd()) }; - - let err = vcpu.configure( - &vm_mem, - GuestAddress(crate::arch::get_kernel_start()), - &vcpu_config, - ); - assert_eq!( - err.unwrap_err(), - KvmVcpuError::ConfigureRegisters(ArchError::SetOneReg( - 0x6030000000100042, - kvm_ioctls::Error::new(9) - )) - ); - } - - #[test] - fn test_init_vcpu() { - let (mut vm, _vm_mem) = setup_vm(0x1000); - let mut vcpu = KvmVcpu::new(0, &vm).unwrap(); - vm.setup_irqchip(1).unwrap(); - - // KVM_ARM_VCPU_PSCI_0_2 is set by default. - // we check if we can remove it. - let vcpu_features = vec![VcpuFeatures { - index: 0, - bitmap: RegisterValueFilter { - filter: 1 << KVM_ARM_VCPU_PSCI_0_2, - value: 0, - }, - }]; - vcpu.init(&vcpu_features).unwrap(); - assert!((vcpu.kvi.features[0] & (1 << KVM_ARM_VCPU_PSCI_0_2)) == 0) - } - - #[test] - fn test_vcpu_save_restore_state() { - let (mut vm, _vm_mem) = setup_vm(0x1000); - let mut vcpu = KvmVcpu::new(0, &vm).unwrap(); - vm.setup_irqchip(1).unwrap(); - - // Calling KVM_GET_REGLIST before KVM_VCPU_INIT will result in error. - let res = vcpu.save_state(); - assert!(matches!( - res.unwrap_err(), - KvmVcpuError::SaveState(ArchError::GetRegList(_)) - )); - - // Try to restore the register using a faulty state. - let mut faulty_vcpu_state = VcpuState::default(); - - // Try faulty kvi state - let res = vcpu.restore_state(&faulty_vcpu_state); - assert!(matches!(res.unwrap_err(), KvmVcpuError::Init(_))); - - // Try faulty vcpu regs - faulty_vcpu_state.kvi = KvmVcpu::default_kvi(vm.fd()).unwrap(); - let mut regs = Aarch64RegisterVec::default(); - let mut reg = Aarch64RegisterRef::new(KVM_REG_SIZE_U64, &[0; 8]); - reg.id = 0; - regs.push(reg); - faulty_vcpu_state.regs = regs; - let res = vcpu.restore_state(&faulty_vcpu_state); - assert!(matches!( - res.unwrap_err(), - KvmVcpuError::RestoreState(ArchError::SetOneReg(0, _)) - )); - - vcpu.init(&[]).unwrap(); - let state = vcpu.save_state().expect("Cannot save state of vcpu"); - assert!(!state.regs.is_empty()); - vcpu.restore_state(&state) - .expect("Cannot restore state of vcpu"); - } - - #[test] - fn test_dump_cpu_config_before_init() { - // Test `dump_cpu_config()` before `KVM_VCPU_INIT`. - // - // This should fail with ENOEXEC. - // https://elixir.bootlin.com/linux/v5.10.176/source/arch/arm64/kvm/arm.c#L1165 - let (mut vm, _vm_mem) = setup_vm(0x1000); - let vcpu = KvmVcpu::new(0, &vm).unwrap(); - vm.setup_irqchip(1).unwrap(); - - vcpu.dump_cpu_config().unwrap_err(); - } - - #[test] - fn test_dump_cpu_config_after_init() { - // Test `dump_cpu_config()` after `KVM_VCPU_INIT`. - let (mut vm, _vm_mem) = setup_vm(0x1000); - let mut vcpu = KvmVcpu::new(0, &vm).unwrap(); - vm.setup_irqchip(1).unwrap(); - vcpu.init(&[]).unwrap(); - - vcpu.dump_cpu_config().unwrap(); - } - - #[test] - fn test_setup_non_boot_vcpu() { - let (vm, _) = setup_vm(0x1000); - let mut vcpu1 = KvmVcpu::new(0, &vm).unwrap(); - vcpu1.init(&[]).unwrap(); - let mut vcpu2 = KvmVcpu::new(1, &vm).unwrap(); - vcpu2.init(&[]).unwrap(); - } - - #[test] - fn test_get_valid_regs() { - // Test `get_regs()` with valid register IDs. - // - X0: 0x6030 0000 0010 0000 - // - X1: 0x6030 0000 0010 0002 - let (_, vcpu, _) = setup_vcpu(0x10000); - let reg_list = Vec::::from([0x6030000000100000, 0x6030000000100002]); - get_registers(&vcpu.fd, ®_list, &mut Aarch64RegisterVec::default()).unwrap(); - } - - #[test] - fn test_get_invalid_regs() { - // Test `get_regs()` with invalid register IDs. - let (_, vcpu, _) = setup_vcpu(0x10000); - let reg_list = Vec::::from([0x6030000000100001, 0x6030000000100003]); - get_registers(&vcpu.fd, ®_list, &mut Aarch64RegisterVec::default()).unwrap_err(); - } -} diff --git a/src/vmm/src/vstate/vm.rs b/src/vmm/src/vstate/vm.rs index 0f72abcf68f..7a8965a4b9a 100644 --- a/src/vmm/src/vstate/vm.rs +++ b/src/vmm/src/vstate/vm.rs @@ -5,601 +5,390 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -#[cfg(target_arch = "x86_64")] -use std::fmt; - -#[cfg(target_arch = "x86_64")] -use kvm_bindings::{ - kvm_clock_data, kvm_irqchip, kvm_pit_config, kvm_pit_state2, CpuId, MsrList, - KVM_CLOCK_TSC_STABLE, KVM_IRQCHIP_IOAPIC, KVM_IRQCHIP_PIC_MASTER, KVM_IRQCHIP_PIC_SLAVE, - KVM_MAX_CPUID_ENTRIES, KVM_PIT_SPEAKER_DUMMY, -}; -use kvm_bindings::{kvm_userspace_memory_region, KVM_API_VERSION, KVM_MEM_LOG_DIRTY_PAGES}; -use kvm_ioctls::{Kvm, VmFd}; -use serde::{Deserialize, Serialize}; - -#[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::gic::GICDevice; -#[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::gic::GicState; -use crate::cpu_config::templates::KvmCapability; -#[cfg(target_arch = "x86_64")] +use std::collections::HashMap; +use std::fs::OpenOptions; +use std::io::Write; +use std::path::Path; +use std::sync::Arc; + +use kvm_bindings::{KVM_MEM_LOG_DIRTY_PAGES, kvm_userspace_memory_region}; +use kvm_ioctls::VmFd; +use vmm_sys_util::eventfd::EventFd; + +pub use crate::arch::{ArchVm as Vm, ArchVmError, VmState}; +use crate::logger::info; +use crate::persist::CreateSnapshotError; use crate::utils::u64_to_usize; -use crate::vstate::memory::{Address, GuestMemory, GuestMemoryMmap, GuestMemoryRegion}; +use crate::vmm_config::snapshot::SnapshotType; +use crate::vstate::memory::{ + Address, GuestMemory, GuestMemoryExtension, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap, +}; +use crate::vstate::vcpu::VcpuError; +use crate::{DirtyBitmap, Vcpu, mem_size_mib}; + +/// Architecture independent parts of a VM. +#[derive(Debug)] +pub struct VmCommon { + /// The KVM file descriptor used to access this Vm. + pub fd: VmFd, + max_memslots: usize, + /// The guest memory of this Vm. + pub guest_memory: GuestMemoryMmap, +} /// Errors associated with the wrappers over KVM ioctls. /// Needs `rustfmt::skip` to make multiline comments work #[rustfmt::skip] -#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)] +#[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum VmError { - /// The host kernel reports an invalid KVM API version: {0} - ApiVersion(i32), - /// Missing KVM capabilities: {0:x?} - Capabilities(u32), - /** Error creating KVM object: {0} Make sure the user launching the firecracker process is \ - configured on the /dev/kvm file's ACL. */ - Kvm(kvm_ioctls::Error), - #[cfg(target_arch = "x86_64")] - /// Failed to get MSR index list to save into snapshots: {0} - GetMsrsToSave(#[from] crate::arch::x86_64::msr::MsrError), - /// The number of configured slots is bigger than the maximum reported by KVM - NotEnoughMemorySlots, /// Cannot set the memory regions: {0} SetUserMemoryRegion(kvm_ioctls::Error), - #[cfg(target_arch = "aarch64")] - /// Error creating the global interrupt controller: {0} - VmCreateGIC(crate::arch::aarch64::gic::GicError), - /// Cannot open the VM file descriptor: {0} - VmFd(kvm_ioctls::Error), - #[cfg(target_arch = "x86_64")] - /// Failed to get KVM vm pit state: {0} - VmGetPit2(kvm_ioctls::Error), - #[cfg(target_arch = "x86_64")] - /// Failed to get KVM vm clock: {0} - VmGetClock(kvm_ioctls::Error), - #[cfg(target_arch = "x86_64")] - /// Failed to get KVM vm irqchip: {0} - VmGetIrqChip(kvm_ioctls::Error), - #[cfg(target_arch = "x86_64")] - /// Failed to set KVM vm pit state: {0} - VmSetPit2(kvm_ioctls::Error), - #[cfg(target_arch = "x86_64")] - /// Failed to set KVM vm clock: {0} - VmSetClock(kvm_ioctls::Error), - #[cfg(target_arch = "x86_64")] - /// Failed to set KVM vm irqchip: {0} - VmSetIrqChip(kvm_ioctls::Error), - /// Cannot configure the microvm: {0} - VmSetup(kvm_ioctls::Error), - #[cfg(target_arch = "aarch64")] - /// Failed to save the VM's GIC state: {0} - SaveGic(crate::arch::aarch64::gic::GicError), - #[cfg(target_arch = "aarch64")] - /// Failed to restore the VM's GIC state: {0} - RestoreGic(crate::arch::aarch64::gic::GicError), -} - -/// Error type for [`Vm::restore_state`] -#[allow(missing_docs)] -#[cfg(target_arch = "x86_64")] -#[derive(Debug, thiserror::Error, displaydoc::Display, PartialEq, Eq)] -pub enum RestoreStateError { - /// Set PIT2 error: {0} - SetPit2(kvm_ioctls::Error), - /// Set clock error: {0} - SetClock(kvm_ioctls::Error), - /// Set IrqChipPicMaster error: {0} - SetIrqChipPicMaster(kvm_ioctls::Error), - /// Set IrqChipPicSlave error: {0} - SetIrqChipPicSlave(kvm_ioctls::Error), - /// Set IrqChipIoAPIC error: {0} - SetIrqChipIoAPIC(kvm_ioctls::Error), - /// VM error: {0} - VmError(VmError), -} - -/// Error type for [`Vm::restore_state`] -#[cfg(target_arch = "aarch64")] -#[derive(Debug, thiserror::Error, displaydoc::Display)] -pub enum RestoreStateError { - /// {0} - GicError(crate::arch::aarch64::gic::GicError), + /// Failed to create VM: {0} + CreateVm(kvm_ioctls::Error), /// {0} - VmError(VmError), -} - -/// A wrapper around creating and using a VM. -#[derive(Debug)] -pub struct Vm { - fd: VmFd, - max_memslots: usize, - - /// Additional capabilities that were specified in cpu template. - pub kvm_cap_modifiers: Vec, - - // X86 specific fields. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - supported_cpuid: CpuId, - #[cfg(target_arch = "x86_64")] - msrs_to_save: MsrList, - - // Arm specific fields. - // On aarch64 we need to keep around the fd obtained by creating the VGIC device. - #[cfg(target_arch = "aarch64")] - irqchip_handle: Option, + Arch(#[from] ArchVmError), + /// Error during eventfd operations: {0} + EventFd(std::io::Error), + /// Failed to create vcpu: {0} + CreateVcpu(VcpuError), + /// The number of configured slots is bigger than the maximum reported by KVM + NotEnoughMemorySlots, + /// Memory Error: {0} + VmMemory(#[from] vm_memory::Error), } /// Contains Vm functions that are usable across CPU architectures impl Vm { - /// Constructs a new `Vm` using the given `Kvm` instance. - pub fn new(kvm_cap_modifiers: Vec) -> Result { - let kvm = Kvm::new().map_err(VmError::Kvm)?; - - // Check that KVM has the correct version. - // Safe to cast because this is a constant. - #[allow(clippy::cast_possible_wrap)] - if kvm.get_api_version() != KVM_API_VERSION as i32 { - return Err(VmError::ApiVersion(kvm.get_api_version())); - } + /// Create a KVM VM + pub fn create_common(kvm: &crate::vstate::kvm::Kvm) -> Result { + // It is known that KVM_CREATE_VM occasionally fails with EINTR on heavily loaded machines + // with many VMs. + // + // The behavior itself that KVM_CREATE_VM can return EINTR is intentional. This is because + // the KVM_CREATE_VM path includes mm_take_all_locks() that is CPU intensive and all CPU + // intensive syscalls should check for pending signals and return EINTR immediately to allow + // userland to remain interactive. + // https://lists.nongnu.org/archive/html/qemu-devel/2014-01/msg01740.html + // + // However, it is empirically confirmed that, even though there is no pending signal, + // KVM_CREATE_VM returns EINTR. + // https://lore.kernel.org/qemu-devel/8735e0s1zw.wl-maz@kernel.org/ + // + // To mitigate it, QEMU does an infinite retry on EINTR that greatly improves reliabiliy: + // - https://github.com/qemu/qemu/commit/94ccff133820552a859c0fb95e33a539e0b90a75 + // - https://github.com/qemu/qemu/commit/bbde13cd14ad4eec18529ce0bf5876058464e124 + // + // Similarly, we do retries up to 5 times. Although Firecracker clients are also able to + // retry, they have to start Firecracker from scratch. Doing retries in Firecracker makes + // recovery faster and improves reliability. + const MAX_ATTEMPTS: u32 = 5; + let mut attempt = 1; + let fd = loop { + match kvm.fd.create_vm() { + Ok(fd) => break fd, + Err(e) if e.errno() == libc::EINTR && attempt < MAX_ATTEMPTS => { + info!("Attempt #{attempt} of KVM_CREATE_VM returned EINTR"); + // Exponential backoff (1us, 2us, 4us, and 8us => 15us in total) + std::thread::sleep(std::time::Duration::from_micros(2u64.pow(attempt - 1))); + } + Err(e) => return Err(VmError::CreateVm(e)), + } - let total_caps = Self::combine_capabilities(&kvm_cap_modifiers); - // Check that all desired capabilities are supported. - Self::check_capabilities(&kvm, &total_caps).map_err(VmError::Capabilities)?; - - let max_memslots = kvm.get_nr_memslots(); - // Create fd for interacting with kvm-vm specific functions. - let vm_fd = kvm.create_vm().map_err(VmError::VmFd)?; - - #[cfg(target_arch = "aarch64")] - { - Ok(Vm { - fd: vm_fd, - max_memslots, - kvm_cap_modifiers, - irqchip_handle: None, - }) - } + attempt += 1; + }; - #[cfg(target_arch = "x86_64")] - { - let supported_cpuid = kvm - .get_supported_cpuid(KVM_MAX_CPUID_ENTRIES) - .map_err(VmError::VmFd)?; - let msrs_to_save = crate::arch::x86_64::msr::get_msrs_to_save(&kvm)?; - - Ok(Vm { - fd: vm_fd, - max_memslots, - kvm_cap_modifiers, - supported_cpuid, - msrs_to_save, - }) - } + Ok(VmCommon { + fd, + max_memslots: kvm.max_nr_memslots(), + guest_memory: GuestMemoryMmap::default(), + }) } - fn combine_capabilities(kvm_cap_modifiers: &[KvmCapability]) -> Vec { - let mut total_caps = Self::DEFAULT_CAPABILITIES.to_vec(); - for modifier in kvm_cap_modifiers.iter() { - match modifier { - KvmCapability::Add(cap) => { - if !total_caps.iter().any(|c| c == cap) { - total_caps.push(*cap); - } - } - KvmCapability::Remove(cap) => { - if let Some(pos) = total_caps.iter().position(|c| c == cap) { - total_caps.remove(pos); - } - } - } - } - total_caps - } + /// Creates the specified number of [`Vcpu`]s. + /// + /// The returned [`EventFd`] is written to whenever any of the vcpus exit. + pub fn create_vcpus(&mut self, vcpu_count: u8) -> Result<(Vec, EventFd), VmError> { + self.arch_pre_create_vcpus(vcpu_count)?; - fn check_capabilities(kvm: &Kvm, capabilities: &[u32]) -> Result<(), u32> { - for cap in capabilities { - // If capability is not supported kernel will return 0. - if kvm.check_extension_raw(u64::from(*cap)) == 0 { - return Err(*cap); - } + let exit_evt = EventFd::new(libc::EFD_NONBLOCK).map_err(VmError::EventFd)?; + + let mut vcpus = Vec::with_capacity(vcpu_count as usize); + for cpu_idx in 0..vcpu_count { + let exit_evt = exit_evt.try_clone().map_err(VmError::EventFd)?; + let vcpu = Vcpu::new(cpu_idx, self, exit_evt).map_err(VmError::CreateVcpu)?; + vcpus.push(vcpu); } - Ok(()) + + self.arch_post_create_vcpus(vcpu_count)?; + + Ok((vcpus, exit_evt)) } - /// Initializes the guest memory. - pub fn memory_init( - &self, - guest_mem: &GuestMemoryMmap, - track_dirty_pages: bool, + /// Register a list of new memory regions to this [`Vm`]. + pub fn register_memory_regions( + &mut self, + regions: Vec, ) -> Result<(), VmError> { - if guest_mem.num_regions() > self.max_memslots { - return Err(VmError::NotEnoughMemorySlots); + for region in regions { + self.register_memory_region(region)? } - self.set_kvm_memory_regions(guest_mem, track_dirty_pages)?; - #[cfg(target_arch = "x86_64")] - self.fd - .set_tss_address(u64_to_usize(crate::arch::x86_64::layout::KVM_TSS_ADDRESS)) - .map_err(VmError::VmSetup)?; Ok(()) } - pub(crate) fn set_kvm_memory_regions( - &self, - guest_mem: &GuestMemoryMmap, - track_dirty_pages: bool, - ) -> Result<(), VmError> { - let mut flags = 0u32; - if track_dirty_pages { - flags |= KVM_MEM_LOG_DIRTY_PAGES; + /// Register a new memory region to this [`Vm`]. + pub fn register_memory_region(&mut self, region: GuestRegionMmap) -> Result<(), VmError> { + let next_slot = self + .guest_memory() + .num_regions() + .try_into() + .map_err(|_| VmError::NotEnoughMemorySlots)?; + if next_slot as usize >= self.common.max_memslots { + return Err(VmError::NotEnoughMemorySlots); } - guest_mem - .iter() - .zip(0u32..) - .try_for_each(|(region, slot)| { - let memory_region = kvm_userspace_memory_region { - slot, - guest_phys_addr: region.start_addr().raw_value(), - memory_size: region.len(), - // It's safe to unwrap because the guest address is valid. - userspace_addr: guest_mem.get_host_address(region.start_addr()).unwrap() as u64, - flags, - }; - - // SAFETY: Safe because the fd is a valid KVM file descriptor. - unsafe { self.fd.set_user_memory_region(memory_region) } - }) - .map_err(VmError::SetUserMemoryRegion)?; - Ok(()) - } - /// Gets a reference to the kvm file descriptor owned by this VM. - pub fn fd(&self) -> &VmFd { - &self.fd - } -} + let flags = if region.bitmap().is_some() { + KVM_MEM_LOG_DIRTY_PAGES + } else { + 0 + }; -#[cfg(target_arch = "aarch64")] -impl Vm { - const DEFAULT_CAPABILITIES: [u32; 7] = [ - kvm_bindings::KVM_CAP_IOEVENTFD, - kvm_bindings::KVM_CAP_IRQFD, - kvm_bindings::KVM_CAP_USER_MEMORY, - kvm_bindings::KVM_CAP_ARM_PSCI_0_2, - kvm_bindings::KVM_CAP_DEVICE_CTRL, - kvm_bindings::KVM_CAP_MP_STATE, - kvm_bindings::KVM_CAP_ONE_REG, - ]; - - /// Creates the GIC (Global Interrupt Controller). - pub fn setup_irqchip(&mut self, vcpu_count: u8) -> Result<(), VmError> { - self.irqchip_handle = Some( - crate::arch::aarch64::gic::create_gic(&self.fd, vcpu_count.into(), None) - .map_err(VmError::VmCreateGIC)?, - ); - Ok(()) - } + let memory_region = kvm_userspace_memory_region { + slot: next_slot, + guest_phys_addr: region.start_addr().raw_value(), + memory_size: region.len(), + userspace_addr: region.as_ptr() as u64, + flags, + }; - /// Gets a reference to the irqchip of the VM. - pub fn get_irqchip(&self) -> &GICDevice { - self.irqchip_handle.as_ref().expect("IRQ chip not set") - } + let new_guest_memory = self.common.guest_memory.insert_region(Arc::new(region))?; - /// Saves and returns the Kvm Vm state. - pub fn save_state(&self, mpidrs: &[u64]) -> Result { - Ok(VmState { - gic: self - .get_irqchip() - .save_device(mpidrs) - .map_err(VmError::SaveGic)?, - kvm_cap_modifiers: self.kvm_cap_modifiers.clone(), - }) - } + // SAFETY: Safe because the fd is a valid KVM file descriptor. + unsafe { + self.fd() + .set_user_memory_region(memory_region) + .map_err(VmError::SetUserMemoryRegion)?; + } + + self.common.guest_memory = new_guest_memory; - /// Restore the KVM VM state - /// - /// # Errors - /// - /// When [`GICDevice::restore_device`] errors. - pub fn restore_state( - &mut self, - mpidrs: &[u64], - state: &VmState, - ) -> Result<(), RestoreStateError> { - self.get_irqchip() - .restore_device(mpidrs, &state.gic) - .map_err(RestoreStateError::GicError)?; Ok(()) } -} -/// Structure holding an general specific VM state. -#[cfg(target_arch = "aarch64")] -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct VmState { - /// GIC state. - pub gic: GicState, - /// Additional capabilities that were specified in cpu template. - pub kvm_cap_modifiers: Vec, -} - -#[cfg(target_arch = "x86_64")] -impl Vm { - const DEFAULT_CAPABILITIES: [u32; 14] = [ - kvm_bindings::KVM_CAP_IRQCHIP, - kvm_bindings::KVM_CAP_IOEVENTFD, - kvm_bindings::KVM_CAP_IRQFD, - kvm_bindings::KVM_CAP_USER_MEMORY, - kvm_bindings::KVM_CAP_SET_TSS_ADDR, - kvm_bindings::KVM_CAP_PIT2, - kvm_bindings::KVM_CAP_PIT_STATE2, - kvm_bindings::KVM_CAP_ADJUST_CLOCK, - kvm_bindings::KVM_CAP_DEBUGREGS, - kvm_bindings::KVM_CAP_MP_STATE, - kvm_bindings::KVM_CAP_VCPU_EVENTS, - kvm_bindings::KVM_CAP_XCRS, - kvm_bindings::KVM_CAP_XSAVE, - kvm_bindings::KVM_CAP_EXT_CPUID, - ]; - - /// Returns a ref to the supported `CpuId` for this Vm. - pub fn supported_cpuid(&self) -> &CpuId { - &self.supported_cpuid + /// Gets a reference to the kvm file descriptor owned by this VM. + pub fn fd(&self) -> &VmFd { + &self.common.fd } - /// Returns a ref to the list of serializable MSR indices. - pub fn msrs_to_save(&self) -> &MsrList { - &self.msrs_to_save + /// Gets a reference to this [`Vm`]'s [`GuestMemoryMmap`] object + pub fn guest_memory(&self) -> &GuestMemoryMmap { + &self.common.guest_memory } - /// Restores the KVM VM state. - /// - /// # Errors - /// - /// When: - /// - [`kvm_ioctls::VmFd::set_pit`] errors. - /// - [`kvm_ioctls::VmFd::set_clock`] errors. - /// - [`kvm_ioctls::VmFd::set_irqchip`] errors. - /// - [`kvm_ioctls::VmFd::set_irqchip`] errors. - /// - [`kvm_ioctls::VmFd::set_irqchip`] errors. - pub fn restore_state(&mut self, state: &VmState) -> Result<(), RestoreStateError> { - self.fd - .set_pit2(&state.pitstate) - .map_err(RestoreStateError::SetPit2)?; - self.fd - .set_clock(&state.clock) - .map_err(RestoreStateError::SetClock)?; - self.fd - .set_irqchip(&state.pic_master) - .map_err(RestoreStateError::SetIrqChipPicMaster)?; - self.fd - .set_irqchip(&state.pic_slave) - .map_err(RestoreStateError::SetIrqChipPicSlave)?; - self.fd - .set_irqchip(&state.ioapic) - .map_err(RestoreStateError::SetIrqChipIoAPIC)?; - Ok(()) + /// Resets the KVM dirty bitmap for each of the guest's memory regions. + pub fn reset_dirty_bitmap(&self) { + self.guest_memory() + .iter() + .zip(0u32..) + .for_each(|(region, slot)| { + let _ = self.fd().get_dirty_log(slot, u64_to_usize(region.len())); + }); } - /// Creates the irq chip and an in-kernel device model for the PIT. - pub fn setup_irqchip(&self) -> Result<(), VmError> { - self.fd.create_irq_chip().map_err(VmError::VmSetup)?; - // We need to enable the emulation of a dummy speaker port stub so that writing to port 0x61 - // (i.e. KVM_SPEAKER_BASE_ADDRESS) does not trigger an exit to user space. - let pit_config = kvm_pit_config { - flags: KVM_PIT_SPEAKER_DUMMY, - ..Default::default() - }; - self.fd.create_pit2(pit_config).map_err(VmError::VmSetup) + /// Retrieves the KVM dirty bitmap for each of the guest's memory regions. + pub fn get_dirty_bitmap(&self) -> Result { + let mut bitmap: DirtyBitmap = HashMap::new(); + self.guest_memory() + .iter() + .zip(0u32..) + .try_for_each(|(region, slot)| { + self.fd() + .get_dirty_log(slot, u64_to_usize(region.len())) + .map(|bitmap_region| _ = bitmap.insert(slot, bitmap_region)) + })?; + Ok(bitmap) } - /// Saves and returns the Kvm Vm state. - pub fn save_state(&self) -> Result { - let pitstate = self.fd.get_pit2().map_err(VmError::VmGetPit2)?; - - let mut clock = self.fd.get_clock().map_err(VmError::VmGetClock)?; - // This bit is not accepted in SET_CLOCK, clear it. - clock.flags &= !KVM_CLOCK_TSC_STABLE; - - let mut pic_master = kvm_irqchip { - chip_id: KVM_IRQCHIP_PIC_MASTER, - ..Default::default() - }; - self.fd - .get_irqchip(&mut pic_master) - .map_err(VmError::VmGetIrqChip)?; + /// Takes a snapshot of the virtual machine running inside the given [`Vmm`] and saves it to + /// `mem_file_path`. + /// + /// If `snapshot_type` is [`SnapshotType::Diff`], and `mem_file_path` exists and is a snapshot + /// file of matching size, then the diff snapshot will be directly merged into the existing + /// snapshot. Otherwise, existing files are simply overwritten. + pub(crate) fn snapshot_memory_to_file( + &self, + mem_file_path: &Path, + snapshot_type: SnapshotType, + ) -> Result<(), CreateSnapshotError> { + use self::CreateSnapshotError::*; + + // Need to check this here, as we create the file in the line below + let file_existed = mem_file_path.exists(); + + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(false) + .open(mem_file_path) + .map_err(|err| MemoryBackingFile("open", err))?; + + // Determine what size our total memory area is. + let mem_size_mib = mem_size_mib(self.guest_memory()); + let expected_size = mem_size_mib * 1024 * 1024; + + if file_existed { + let file_size = file + .metadata() + .map_err(|e| MemoryBackingFile("get_metadata", e))? + .len(); + + // Here we only truncate the file if the size mismatches. + // - For full snapshots, the entire file's contents will be overwritten anyway. We have + // to avoid truncating here to deal with the edge case where it represents the + // snapshot file from which this very microVM was loaded (as modifying the memory file + // would be reflected in the mmap of the file, meaning a truncate operation would zero + // out guest memory, and thus corrupt the VM). + // - For diff snapshots, we want to merge the diff layer directly into the file. + if file_size != expected_size { + file.set_len(0) + .map_err(|err| MemoryBackingFile("truncate", err))?; + } + } - let mut pic_slave = kvm_irqchip { - chip_id: KVM_IRQCHIP_PIC_SLAVE, - ..Default::default() - }; - self.fd - .get_irqchip(&mut pic_slave) - .map_err(VmError::VmGetIrqChip)?; + // Set the length of the file to the full size of the memory area. + file.set_len(expected_size) + .map_err(|e| MemoryBackingFile("set_length", e))?; - let mut ioapic = kvm_irqchip { - chip_id: KVM_IRQCHIP_IOAPIC, - ..Default::default() + match snapshot_type { + SnapshotType::Diff => { + let dirty_bitmap = self.get_dirty_bitmap()?; + self.guest_memory().dump_dirty(&mut file, &dirty_bitmap)?; + } + SnapshotType::Full => { + self.guest_memory().dump(&mut file)?; + self.reset_dirty_bitmap(); + self.guest_memory().reset_dirty(); + } }; - self.fd - .get_irqchip(&mut ioapic) - .map_err(VmError::VmGetIrqChip)?; - - Ok(VmState { - pitstate, - clock, - pic_master, - pic_slave, - ioapic, - kvm_cap_modifiers: self.kvm_cap_modifiers.clone(), - }) - } -} - -#[cfg(target_arch = "x86_64")] -#[derive(Default, Deserialize, Serialize)] -/// Structure holding VM kvm state. -pub struct VmState { - pitstate: kvm_pit_state2, - clock: kvm_clock_data, - // TODO: rename this field to adopt inclusive language once Linux updates it, too. - pic_master: kvm_irqchip, - // TODO: rename this field to adopt inclusive language once Linux updates it, too. - pic_slave: kvm_irqchip, - ioapic: kvm_irqchip, - - /// Additional capabilities that were specified in cpu template. - pub kvm_cap_modifiers: Vec, -} -#[cfg(target_arch = "x86_64")] -impl fmt::Debug for VmState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("VmState") - .field("pitstate", &self.pitstate) - .field("clock", &self.clock) - .field("pic_master", &"?") - .field("pic_slave", &"?") - .field("ioapic", &"?") - .finish() + file.flush() + .map_err(|err| MemoryBackingFile("flush", err))?; + file.sync_all() + .map_err(|err| MemoryBackingFile("sync_all", err)) } } #[cfg(test)] pub(crate) mod tests { + use vm_memory::GuestAddress; + use vm_memory::mmap::MmapRegionBuilder; + use super::*; - #[cfg(target_arch = "x86_64")] - use crate::snapshot::Snapshot; - use crate::test_utils::single_region_mem; - use crate::vstate::memory::GuestMemoryMmap; + use crate::test_utils::single_region_mem_raw; + use crate::utils::mib_to_bytes; + use crate::vstate::kvm::Kvm; + use crate::vstate::memory::GuestRegionMmap; // Auxiliary function being used throughout the tests. - pub(crate) fn setup_vm(mem_size: usize) -> (Vm, GuestMemoryMmap) { - let gm = single_region_mem(mem_size); - - let vm = Vm::new(vec![]).expect("Cannot create new vm"); - vm.memory_init(&gm, false).unwrap(); + pub(crate) fn setup_vm() -> (Kvm, Vm) { + let kvm = Kvm::new(vec![]).expect("Cannot create Kvm"); + let vm = Vm::new(&kvm).expect("Cannot create new vm"); + (kvm, vm) + } - (vm, gm) + // Auxiliary function being used throughout the tests. + pub(crate) fn setup_vm_with_memory(mem_size: usize) -> (Kvm, Vm) { + let (kvm, mut vm) = setup_vm(); + let gm = single_region_mem_raw(mem_size); + vm.register_memory_regions(gm).unwrap(); + (kvm, vm) } #[test] fn test_new() { // Testing with a valid /dev/kvm descriptor. - Vm::new(vec![]).unwrap(); + let kvm = Kvm::new(vec![]).expect("Cannot create Kvm"); + Vm::new(&kvm).unwrap(); } #[test] - fn test_combine_capabilities() { - // Default caps for x86_64 and aarch64 both have KVM_CAP_IOEVENTFD and don't have - // KVM_CAP_IOMMU caps. - let additional_capabilities = vec![ - KvmCapability::Add(kvm_bindings::KVM_CAP_IOMMU), - KvmCapability::Remove(kvm_bindings::KVM_CAP_IOEVENTFD), - ]; - - let combined_caps = Vm::combine_capabilities(&additional_capabilities); - assert!(combined_caps - .iter() - .any(|c| *c == kvm_bindings::KVM_CAP_IOMMU)); - assert!(!combined_caps - .iter() - .any(|c| *c == kvm_bindings::KVM_CAP_IOEVENTFD)); - } + fn test_register_memory_regions() { + let (_, mut vm) = setup_vm(); - #[test] - fn test_vm_memory_init() { - let vm = Vm::new(vec![]).expect("Cannot create new vm"); - - // Create valid memory region and test that the initialization is successful. - let gm = single_region_mem(0x1000); - vm.memory_init(&gm, true).unwrap(); - } - - #[cfg(target_arch = "x86_64")] - #[test] - fn test_vm_save_restore_state() { - let vm = Vm::new(vec![]).expect("new vm failed"); - // Irqchips, clock and pitstate are not configured so trying to save state should fail. - vm.save_state().unwrap_err(); - - let (vm, _mem) = setup_vm(0x1000); - vm.setup_irqchip().unwrap(); - - let vm_state = vm.save_state().unwrap(); + // Trying to set a memory region with a size that is not a multiple of GUEST_PAGE_SIZE + // will result in error. + let gm = single_region_mem_raw(0x10); + let res = vm.register_memory_regions(gm); assert_eq!( - vm_state.pitstate.flags | KVM_PIT_SPEAKER_DUMMY, - KVM_PIT_SPEAKER_DUMMY + res.unwrap_err().to_string(), + "Cannot set the memory regions: Invalid argument (os error 22)" ); - assert_eq!(vm_state.clock.flags & KVM_CLOCK_TSC_STABLE, 0); - assert_eq!(vm_state.pic_master.chip_id, KVM_IRQCHIP_PIC_MASTER); - assert_eq!(vm_state.pic_slave.chip_id, KVM_IRQCHIP_PIC_SLAVE); - assert_eq!(vm_state.ioapic.chip_id, KVM_IRQCHIP_IOAPIC); - - let (mut vm, _mem) = setup_vm(0x1000); - vm.setup_irqchip().unwrap(); - - vm.restore_state(&vm_state).unwrap(); - } - #[cfg(target_arch = "x86_64")] - #[test] - fn test_vm_save_restore_state_bad_irqchip() { - use kvm_bindings::KVM_NR_IRQCHIPS; - - let (vm, _mem) = setup_vm(0x1000); - vm.setup_irqchip().unwrap(); - let mut vm_state = vm.save_state().unwrap(); - - let (mut vm, _mem) = setup_vm(0x1000); - vm.setup_irqchip().unwrap(); - - // Try to restore an invalid PIC Master chip ID - let orig_master_chip_id = vm_state.pic_master.chip_id; - vm_state.pic_master.chip_id = KVM_NR_IRQCHIPS; - vm.restore_state(&vm_state).unwrap_err(); - vm_state.pic_master.chip_id = orig_master_chip_id; - - // Try to restore an invalid PIC Slave chip ID - let orig_slave_chip_id = vm_state.pic_slave.chip_id; - vm_state.pic_slave.chip_id = KVM_NR_IRQCHIPS; - vm.restore_state(&vm_state).unwrap_err(); - vm_state.pic_slave.chip_id = orig_slave_chip_id; - - // Try to restore an invalid IOPIC chip ID - vm_state.ioapic.chip_id = KVM_NR_IRQCHIPS; - vm.restore_state(&vm_state).unwrap_err(); + let gm = single_region_mem_raw(0x1000); + let res = vm.register_memory_regions(gm); + res.unwrap(); } - #[cfg(target_arch = "x86_64")] #[test] - fn test_vmstate_serde() { - let mut snapshot_data = vec![0u8; 10000]; - - let (mut vm, _) = setup_vm(0x1000); - vm.setup_irqchip().unwrap(); - let state = vm.save_state().unwrap(); - Snapshot::serialize(&mut snapshot_data.as_mut_slice(), &state).unwrap(); - let restored_state: VmState = Snapshot::deserialize(&mut snapshot_data.as_slice()).unwrap(); + fn test_too_many_regions() { + let (kvm, mut vm) = setup_vm(); + let max_nr_regions = kvm.max_nr_memslots(); + + // SAFETY: valid mmap parameters + let ptr = unsafe { + libc::mmap( + std::ptr::null_mut(), + 0x1000, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, + -1, + 0, + ) + }; - vm.restore_state(&restored_state).unwrap(); + assert_ne!(ptr, libc::MAP_FAILED); + + for i in 0..=max_nr_regions { + // SAFETY: we assert above that the ptr is valid, and the size matches what we passed to + // mmap + let region = unsafe { + MmapRegionBuilder::new(0x1000) + .with_raw_mmap_pointer(ptr.cast()) + .build() + .unwrap() + }; + + let region = GuestRegionMmap::new(region, GuestAddress(i as u64 * 0x1000)).unwrap(); + + let res = vm.register_memory_region(region); + + if i >= max_nr_regions { + assert!( + matches!(res, Err(VmError::NotEnoughMemorySlots)), + "{:?} at iteration {} - max_nr_memslots: {}", + res, + i, + max_nr_regions + ); + } else { + res.unwrap_or_else(|_| { + panic!( + "to be able to insert more regions in iteration {i} - max_nr_memslots: \ + {max_nr_regions} - num_regions: {}", + vm.guest_memory().num_regions() + ) + }); + } + } } #[test] - fn test_set_kvm_memory_regions() { - let vm = Vm::new(vec![]).expect("Cannot create new vm"); + fn test_create_vcpus() { + let vcpu_count = 2; + let (_, mut vm) = setup_vm_with_memory(mib_to_bytes(128)); - let gm = single_region_mem(0x1000); - let res = vm.set_kvm_memory_regions(&gm, false); - res.unwrap(); + let (vcpu_vec, _) = vm.create_vcpus(vcpu_count).unwrap(); - // Trying to set a memory region with a size that is not a multiple of PAGE_SIZE - // will result in error. - let gm = single_region_mem(0x10); - let res = vm.set_kvm_memory_regions(&gm, false); - assert_eq!( - res.unwrap_err().to_string(), - "Cannot set the memory regions: Invalid argument (os error 22)" - ); + assert_eq!(vcpu_vec.len(), vcpu_count as usize); } } diff --git a/src/vmm/tests/integration_tests.rs b/src/vmm/tests/integration_tests.rs index 4312c6345db..55fb07c1aae 100644 --- a/src/vmm/tests/integration_tests.rs +++ b/src/vmm/tests/integration_tests.rs @@ -7,22 +7,20 @@ use std::time::Duration; use vmm::builder::build_and_boot_microvm; use vmm::devices::virtio::block::CacheType; -use vmm::persist::{snapshot_state_sanity_check, MicrovmState, MicrovmStateError, VmInfo}; +use vmm::persist::{MicrovmState, MicrovmStateError, VmInfo, snapshot_state_sanity_check}; use vmm::resources::VmResources; use vmm::rpc_interface::{ LoadSnapshotError, PrebootApiController, RuntimeApiController, VmmAction, VmmActionError, }; -use vmm::seccomp_filters::get_empty_filters; +use vmm::seccomp::get_empty_filters; use vmm::snapshot::Snapshot; -#[cfg(target_arch = "x86_64")] -use vmm::test_utils::dirty_tracking_vmm; use vmm::test_utils::mock_resources::{MockVmResources, NOISY_KERNEL_IMAGE}; use vmm::test_utils::{create_vmm, default_vmm, default_vmm_no_boot}; use vmm::vmm_config::balloon::BalloonDeviceConfig; use vmm::vmm_config::boot_source::BootSourceConfig; use vmm::vmm_config::drive::BlockDeviceConfig; use vmm::vmm_config::instance_info::{InstanceInfo, VmState}; -use vmm::vmm_config::machine_config::{MachineConfig, MachineConfigUpdate, VmConfig}; +use vmm::vmm_config::machine_config::{MachineConfig, MachineConfigUpdate}; use vmm::vmm_config::net::NetworkInterfaceConfig; use vmm::vmm_config::snapshot::{ CreateSnapshotParams, LoadSnapshotParams, MemBackendConfig, MemBackendType, SnapshotType, @@ -112,8 +110,13 @@ fn test_dirty_bitmap_error() { // with errno 2 (ENOENT) because KVM can't find any guest memory regions with dirty // page tracking enabled. assert_eq!( - format!("{:?}", vmm.lock().unwrap().get_dirty_bitmap().err()), - "Some(DirtyBitmap(Error(2)))" + vmm.lock() + .unwrap() + .vm + .get_dirty_bitmap() + .unwrap_err() + .errno(), + 2 ); vmm.lock().unwrap().stop(FcExitCode::Ok); } @@ -122,11 +125,11 @@ fn test_dirty_bitmap_error() { #[cfg(target_arch = "x86_64")] fn test_dirty_bitmap_success() { // The vmm will start with dirty page tracking = ON. - let (vmm, _) = dirty_tracking_vmm(Some(NOISY_KERNEL_IMAGE)); + let (vmm, _) = vmm::test_utils::dirty_tracking_vmm(Some(NOISY_KERNEL_IMAGE)); // Let it churn for a while and dirty some pages... thread::sleep(Duration::from_millis(100)); - let bitmap = vmm.lock().unwrap().get_dirty_bitmap().unwrap(); + let bitmap = vmm.lock().unwrap().vm.get_dirty_bitmap().unwrap(); let num_dirty_pages: u32 = bitmap .values() .map(|bitmap_per_region| { @@ -188,7 +191,7 @@ fn verify_create_snapshot(is_diff: bool) -> (TempFile, TempFile) { let (vmm, _) = create_vmm(Some(NOISY_KERNEL_IMAGE), is_diff, true); let resources = VmResources { - vm_config: VmConfig { + machine_config: MachineConfig { mem_size_mib: 1, track_dirty_pages: is_diff, ..Default::default() @@ -261,6 +264,7 @@ fn verify_load_snapshot(snapshot_file: TempFile, memory_file: TempFile) { }, enable_diff_snapshots: false, resume_vm: true, + network_overrides: vec![], })) .unwrap(); @@ -298,7 +302,7 @@ fn test_snapshot_load_sanity_checks() { snapshot_state_sanity_check(µvm_state).unwrap(); // Remove memory regions. - microvm_state.memory_state.regions.clear(); + microvm_state.vm_state.memory.regions.clear(); // Validate sanity checks fail because there is no mem region in state. assert_eq!( @@ -344,6 +348,7 @@ fn verify_load_snap_disallowed_after_boot_resources(res: VmmAction, res_name: &s }, enable_diff_snapshots: false, resume_vm: false, + network_overrides: vec![], }); let err = preboot_api_controller.handle_preboot_request(req); assert!( @@ -403,6 +408,7 @@ fn test_preboot_load_snap_disallowed_after_boot_resources() { }); verify_load_snap_disallowed_after_boot_resources(req, "SetVsockDevice"); - let req = VmmAction::UpdateVmConfiguration(MachineConfigUpdate::from(MachineConfig::default())); + let req = + VmmAction::UpdateMachineConfiguration(MachineConfigUpdate::from(MachineConfig::default())); verify_load_snap_disallowed_after_boot_resources(req, "SetVmConfiguration"); } diff --git a/tests/README.md b/tests/README.md index 4fee1baee30..1d7b7075f62 100644 --- a/tests/README.md +++ b/tests/README.md @@ -142,6 +142,21 @@ above when run on a PR will fail iff a newly added dependency has a known open RustSec advisory. If run outside a PR, it will fail if any existing dependency has an open RustSec advisory). +### Functional A/B-Tests + +Firecracker has some functional A/B-tests (for example, in +`test_vulnerabilities.py`), which generally compare the state of the pull +request target branch (e.g. `main`), with the PR head. However, when running +these locally, pytest does not know anything about potential PRs that the commit +the tests are being run on are contained in, and as such cannot do this +A/B-Test. To run functional A/B-Tests locally, you need to create a "fake" PR +environment by setting the `BUILDKITE_PULL_REQUEST` and +`BUILDKITE_PULL_REQUEST_BASE_BRANCH` environment variables: + +``` +BUILDKITE_PULL_REQUEST=true BUILDKITE_PULL_REQUEST_BASE_BRANCH=main ./tools/devtool test -- integration_tests/security/test_vulnerabilities.py +``` + ### Performance A/B-Tests Firecracker has a special framework for orchestrating long-running A/B-tests @@ -303,13 +318,17 @@ that are pre-initialized with specific guest kernels and rootfs: - `uvm_plain_any` is parametrized by the guest kernels [supported](../docs/kernel-policy.md) by Firecracker and a read-only Ubuntu - 22.04 squashfs as rootfs, + 24.04 squashfs as rootfs, - `uvm_plain` yields a Firecracker process pre-initialized with a 5.10 kernel - and the same Ubuntu 22.04 squashfs. + and the same Ubuntu 24.04 squashfs. +- `uvm_any` yields started microvms, parametrized by all supported kernels, all + CPU templates (static, custom and none), and either booted or restored from a + snapshot. +- `uvm_any_booted` works the same as `uvm_any`, but only for booted VMs. -Generally, tests should use the former if you are testing some interaction -between the guest and Firecracker, while the latter should be used if -Firecracker functionality unrelated to the guest is being tested. +Generally, tests should use `uvm_plain_any` if you are testing some interaction +between the guest and Firecracker, and `uvm_plain` should be used if Firecracker +functionality unrelated to the guest is being tested. ### Markers @@ -396,36 +415,33 @@ setting to achieve consistent performance. Please see the `test` section of `Q1:` *I have a shell script that runs my tests and I don't want to rewrite it.*\ -`A1:` Insofar as it makes sense, you should write it as a python test -function. However, you can always call the script from a shim python test -function. You can also add it as a microvm image resource in the s3 bucket (and -it will be made available under `microvm.slot.path`) or copy it over to a guest -filesystem as part of your test. +`A1:` Insofar as it makes sense, you should write it as a python test function. +However, you can always call the script from a shim python test function. You +can also add it as a microvm image resource in the s3 bucket (and it will be +made available under `microvm.slot.path`) or copy it over to a guest filesystem +as part of your test. `Q2:` *I want to add more tests that I don't want to commit to the Firecracker repository.*\ -`A2:` Before a testrun or test session, just add your test -directory under `tests/`. `pytest` will discover all tests in this tree. +`A2:` Before a testrun or test session, just add your test directory under +`tests/`. `pytest` will discover all tests in this tree. -`Q3:` *I want to have my own test fixtures, and not commit them in the -repo.*\ -`A3:` Add a `conftest.py` file in your test directory, and place your -fixtures there. `pytest` will bring them into scope for all your tests. +`Q3:` *I want to have my own test fixtures, and not commit them in the repo.*\ +`A3:` Add a `conftest.py` file in your test directory, and place your fixtures +there. `pytest` will bring them into scope for all your tests. `Q4:` *I want to use more/other microvm test images, but I don't want to add them to the common s3 bucket.*\ -`A4:` Add your custom images to the `build/img` -subdirectory in the Firecracker source tree. This directory is bind-mounted in -the container and used as a local image cache. +`A4:` Add your custom images to the `build/img` subdirectory in the Firecracker +source tree. This directory is bind-mounted in the container and used as a local +image cache. `Q5:` *How can I get live logger output from the tests?*\ -`A5:` Accessing -**pytest.ini** will allow you to modify logger settings. +`A5:` Accessing **pytest.ini** will allow you to modify logger settings. `Q6:` *Is there a way to speed up integration tests execution time?*\ -`A6:` You -can narrow down the test selection as described in the **Running** section. For -example: +`A6:` You can narrow down the test selection as described in the **Running** +section. For example: 1. Pass the `-k substring` option to pytest to only run a subset of tests by specifying a part of their name. @@ -639,6 +655,6 @@ sudo pip3 install pytest ipython requests psutil tenacity filelock "urllib3<2.0" sudo env PYTHONPATH=tests HOME=$HOME ~/.local/bin/ipython3 -i tools/sandbox.py -- --binary-dir ../repro/v1.4.1 ``` -> \[!WARNING\] +> [!WARNING] > > **Notice this runs as root!** diff --git a/tests/conftest.py b/tests/conftest.py index c77fee09039..bfc6c6cedd7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,6 @@ # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -# We import some fixtures that are unused. Disable that too. -# pylint:disable=unused-import - """Imported by pytest at the start of every test session. # Fixture Goals @@ -23,19 +20,20 @@ """ import inspect +import json import os -import re +import platform import shutil import sys import tempfile from pathlib import Path -from typing import Dict import pytest import host_tools.cargo_build as build_tools from framework import defs, utils -from framework.artifacts import kernel_params, rootfs_params +from framework.artifacts import disks, kernel_params +from framework.defs import DEFAULT_BINARY_DIR from framework.microvm import MicroVMFactory from framework.properties import global_props from framework.utils_cpu_templates import ( @@ -43,6 +41,7 @@ static_cpu_templates_params, ) from host_tools.metrics import get_metrics_logger +from host_tools.network import NetNs # This codebase uses Python features available in Python 3.10 or above if sys.version_info < (3, 10): @@ -55,7 +54,7 @@ METRICS = get_metrics_logger() -PHASE_REPORT_KEY = pytest.StashKey[Dict[str, pytest.CollectReport]]() +PHASE_REPORT_KEY = pytest.StashKey[dict[str, pytest.CollectReport]]() def pytest_addoption(parser): @@ -66,6 +65,19 @@ def pytest_addoption(parser): help="use firecracker/jailer binaries from this directory instead of compiling from source", ) + parser.addoption( + "--custom-cpu-template", + action="store", + help="Path to custom CPU template to be applied unless overwritten by a test", + default=None, + type=Path, + ) + + +def pytest_report_header(): + """Pytest hook to print relevant metadata in the logs""" + return f"EC2 AMI: {global_props.ami}" + @pytest.hookimpl(wrapper=True, tryfirst=True) def pytest_runtest_makereport(item, call): # pylint:disable=unused-argument @@ -105,6 +117,11 @@ def record_props(request, record_property): def pytest_runtest_logreport(report): """Send general test metrics to CloudWatch""" + # only publish metrics from the main process + worker_id = os.environ.get("PYTEST_XDIST_WORKER") + if worker_id is not None: + return + # The pytest's test protocol has three phases for each test item: setup, # call and teardown. At the end of each phase, pytest_runtest_logreport() # is called. @@ -135,6 +152,7 @@ def pytest_runtest_logreport(report): # and global {}, ) + METRICS.set_property("pytest_xdist_worker", worker_id) METRICS.set_property("result", report.outcome) METRICS.set_property("location", report.location) for prop_name, prop_val in report.user_properties: @@ -235,48 +253,83 @@ def bin_seccomp_paths(): yield demos -@pytest.fixture -def uffd_handler_paths(): - """Build UFFD handler binaries.""" - handlers = { - f"{handler}_handler": build_tools.get_example(f"uffd_{handler}_handler") - for handler in ["malicious", "valid", "fault_all"] - } - yield handlers +@pytest.fixture(scope="session") +def netns_factory(worker_id): + """A network namespace factory + + Network namespaces are created once per test session and re-used in subsequent tests. + """ + # pylint:disable=protected-access + + class NetNsFactory: + """A Network namespace factory that reuses namespaces.""" + + def __init__(self, prefix: str): + self._all = [] + self._returned = [] + self.prefix = prefix + + def get(self, _netns_id): + """Get a free network namespace""" + if len(self._returned) > 0: + ns = self._returned.pop(0) + while ns.is_used(): + pass + return ns + ns = NetNs(self.prefix + str(len(self._all))) + # change the cleanup function so it is returned to the pool + ns._cleanup_orig = ns.cleanup + ns.cleanup = lambda: self._returned.append(ns) + self._all.append(ns) + return ns + + netns_fcty = NetNsFactory(f"netns-{worker_id}-") + yield netns_fcty.get + + for netns in netns_fcty._all: + netns._cleanup_orig() @pytest.fixture() -def microvm_factory(request, record_property, results_dir): - """Fixture to create microvms simply. +def microvm_factory(request, record_property, results_dir, netns_factory): + """Fixture to create microvms simply.""" - In order to avoid running out of space when instantiating many microvms, - we remove the directory manually when the fixture is destroyed - (that is after every test). - One can comment the removal line, if it helps with debugging. - """ + binary_dir = request.config.getoption("--binary-dir") or DEFAULT_BINARY_DIR + if isinstance(binary_dir, str): + binary_dir = Path(binary_dir) - if binary_dir := request.config.getoption("--binary-dir"): - fc_binary_path = Path(binary_dir) / "firecracker" - jailer_binary_path = Path(binary_dir) / "jailer" - if not fc_binary_path.exists(): - raise RuntimeError("Firecracker binary does not exist") - else: - fc_binary_path, jailer_binary_path = build_tools.get_firecracker_binaries() - record_property("firecracker_bin", str(fc_binary_path)) + record_property("firecracker_bin", str(binary_dir / "firecracker")) + # If `--custom-cpu-template` option is provided, the given CPU template will + # be applied afterwards unless overwritten. + custom_cpu_template_path = request.config.getoption("--custom-cpu-template") + custom_cpu_template = ( + { + "name": custom_cpu_template_path.stem, + "template": json.loads(custom_cpu_template_path.read_text("utf-8")), + } + if custom_cpu_template_path + else None + ) # We could override the chroot base like so # jailer_kwargs={"chroot_base": "/srv/jailo"} - uvm_factory = MicroVMFactory(fc_binary_path, jailer_binary_path) + uvm_factory = MicroVMFactory( + binary_dir, + netns_factory=netns_factory, + custom_cpu_template=custom_cpu_template, + ) yield uvm_factory # if the test failed, save important files from the root of the uVM into `test_results` for troubleshooting report = request.node.stash[PHASE_REPORT_KEY] if "call" in report and report["call"].failed: - dmesg = utils.run_cmd(["dmesg", "-dPx"]) for uvm in uvm_factory.vms: uvm_data = results_dir / uvm.id uvm_data.mkdir() - uvm_data.joinpath("host-dmesg.log").write_text(dmesg.stdout) + uvm_data.joinpath("host-dmesg.log").write_text( + utils.run_cmd(["dmesg", "-dPx"]).stdout + ) + shutil.copy(f"/firecracker/build/img/{platform.machine()}/id_rsa", uvm_data) uvm_root = Path(uvm.chroot()) for item in os.listdir(uvm_root): @@ -284,7 +337,7 @@ def microvm_factory(request, record_property, results_dir): if not os.path.isfile(src): continue dst = uvm_data / item - shutil.copy(src, dst) + shutil.move(src, dst) console_data = uvm.console_data if console_data: uvm_data.joinpath("guest-console.log").write_text(console_data) @@ -292,13 +345,6 @@ def microvm_factory(request, record_property, results_dir): uvm_factory.kill() -@pytest.fixture(params=static_cpu_templates_params()) -def cpu_template(request, record_property): - """Return all static CPU templates supported by the vendor.""" - record_property("static_cpu_template", request.param) - return request.param - - @pytest.fixture(params=custom_cpu_templates_params()) def custom_cpu_template(request, record_property): """Return all dummy custom CPU templates supported by the vendor.""" @@ -307,22 +353,26 @@ def custom_cpu_template(request, record_property): @pytest.fixture( - params=list(static_cpu_templates_params()) + list(custom_cpu_templates_params()) + params=[ + pytest.param(None, id="NO_CPU_TMPL"), + *static_cpu_templates_params(), + *custom_cpu_templates_params(), + ], ) def cpu_template_any(request, record_property): - """This fixture combines static and custom CPU templates""" - if "name" in request.param: - record_property("custom_cpu_template", request.param["name"]) - else: - record_property("static_cpu_template", request.param) + """This fixture combines no template, static and custom CPU templates""" + cpu_template_name = request.param + if request.param is None: + cpu_template_name = "None" + elif "name" in request.param: + cpu_template_name = request.param["name"] + record_property("cpu_template", cpu_template_name) return request.param @pytest.fixture(params=["Sync", "Async"]) def io_engine(request): """All supported io_engines""" - if request.param == "Async" and not utils.is_io_uring_supported(): - pytest.skip("io_uring not supported in this kernel") return request.param @@ -355,13 +405,6 @@ def guest_kernel_fxt(request, record_property): return kernel -def rootfs_fxt(request, record_property): - """Return all supported rootfs.""" - fs = request.param - record_property("rootfs", fs.name) - return fs - - # Fixtures for all guest kernels, and specific versions guest_kernel = pytest.fixture(guest_kernel_fxt, params=kernel_params("vmlinux-*")) guest_kernel_acpi = pytest.fixture( @@ -381,29 +424,28 @@ def rootfs_fxt(request, record_property): params=kernel_params("vmlinux-6.1*"), ) -# Fixtures for all Ubuntu rootfs, and specific versions -rootfs = pytest.fixture(rootfs_fxt, params=rootfs_params("*.squashfs")) -rootfs_ubuntu_22 = pytest.fixture( - rootfs_fxt, params=rootfs_params("ubuntu-22*.squashfs") -) -rootfs_rw = pytest.fixture(rootfs_fxt, params=rootfs_params("*.ext4")) + +@pytest.fixture +def rootfs(): + """Return an Ubuntu 24.04 read-only rootfs""" + return disks("ubuntu-24.04.squashfs")[0] @pytest.fixture -def uvm_plain(microvm_factory, guest_kernel_linux_5_10, rootfs_ubuntu_22): - """Create a vanilla VM, non-parametrized - kernel: 5.10 - rootfs: Ubuntu 22.04 - """ - return microvm_factory.build(guest_kernel_linux_5_10, rootfs_ubuntu_22) +def rootfs_rw(): + """Return an Ubuntu 24.04 ext4 rootfs""" + return disks("ubuntu-24.04.ext4")[0] + + +@pytest.fixture +def uvm_plain(microvm_factory, guest_kernel_linux_5_10, rootfs): + """Create a vanilla VM, non-parametrized""" + return microvm_factory.build(guest_kernel_linux_5_10, rootfs) @pytest.fixture def uvm_plain_rw(microvm_factory, guest_kernel_linux_5_10, rootfs_rw): - """Create a vanilla VM, non-parametrized - kernel: 5.10 - rootfs: Ubuntu 22.04 - """ + """Create a vanilla VM, non-parametrized""" return microvm_factory.build(guest_kernel_linux_5_10, rootfs_rw) @@ -424,23 +466,98 @@ def artifact_dir(): @pytest.fixture -def uvm_plain_any(microvm_factory, guest_kernel, rootfs_ubuntu_22): +def uvm_plain_any(microvm_factory, guest_kernel, rootfs): """All guest kernels kernel: all - rootfs: Ubuntu 22.04 + rootfs: Ubuntu 24.04 """ - return microvm_factory.build(guest_kernel, rootfs_ubuntu_22) + return microvm_factory.build(guest_kernel, rootfs) + + +guest_kernel_6_1_debug = pytest.fixture( + guest_kernel_fxt, + params=kernel_params("vmlinux-6.1*", artifact_dir=defs.ARTIFACT_DIR / "debug"), +) + + +@pytest.fixture +def uvm_plain_debug(microvm_factory, guest_kernel_6_1_debug, rootfs_rw): + """VM running a kernel with debug/trace Kconfig options""" + return microvm_factory.build(guest_kernel_6_1_debug, rootfs_rw) + + +@pytest.fixture +def vcpu_count(): + """Return default vcpu_count. Use indirect parametrization to override.""" + return 2 @pytest.fixture -def uvm_with_initrd( - microvm_factory, guest_kernel_linux_5_10, record_property, artifact_dir +def mem_size_mib(): + """Return memory size. Use indirect parametrization to override.""" + return 256 + + +def uvm_booted( + microvm_factory, guest_kernel, rootfs, cpu_template, vcpu_count=2, mem_size_mib=256 ): - """ - See file:../docs/initrd.md - """ - fs = artifact_dir / "initramfs.cpio" - record_property("rootfs", fs.name) - uvm = microvm_factory.build(guest_kernel_linux_5_10) - uvm.initrd_file = fs - yield uvm + """Return a booted uvm""" + uvm = microvm_factory.build(guest_kernel, rootfs) + uvm.spawn() + uvm.basic_config(vcpu_count=vcpu_count, mem_size_mib=mem_size_mib) + uvm.set_cpu_template(cpu_template) + uvm.add_net_iface() + uvm.start() + return uvm + + +def uvm_restored(microvm_factory, guest_kernel, rootfs, cpu_template, **kwargs): + """Return a restored uvm""" + uvm = uvm_booted(microvm_factory, guest_kernel, rootfs, cpu_template, **kwargs) + snapshot = uvm.snapshot_full() + uvm.kill() + uvm2 = microvm_factory.build_from_snapshot(snapshot) + uvm2.cpu_template_name = uvm.cpu_template_name + return uvm2 + + +@pytest.fixture(params=[uvm_booted, uvm_restored]) +def uvm_ctor(request): + """Fixture to return uvms with different constructors""" + return request.param + + +@pytest.fixture +def uvm_any( + microvm_factory, + uvm_ctor, + guest_kernel, + rootfs, + cpu_template_any, + vcpu_count, + mem_size_mib, +): + """Return booted and restored uvms""" + return uvm_ctor( + microvm_factory, + guest_kernel, + rootfs, + cpu_template_any, + vcpu_count=vcpu_count, + mem_size_mib=mem_size_mib, + ) + + +@pytest.fixture +def uvm_any_booted( + microvm_factory, guest_kernel, rootfs, cpu_template_any, vcpu_count, mem_size_mib +): + """Return booted uvms""" + return uvm_booted( + microvm_factory, + guest_kernel, + rootfs, + cpu_template_any, + vcpu_count=vcpu_count, + mem_size_mib=mem_size_mib, + ) diff --git a/tests/data/cpu_template_helper/fingerprint_AMD_GENOA_5.10host.json b/tests/data/cpu_template_helper/fingerprint_AMD_GENOA_5.10host.json new file mode 100644 index 00000000000..7b835e165b8 --- /dev/null +++ b/tests/data/cpu_template_helper/fingerprint_AMD_GENOA_5.10host.json @@ -0,0 +1,1591 @@ +{ + "firecracker_version": "1.12.0-dev", + "kernel_version": "5.10.234-225.910.amzn2.x86_64", + "microcode_version": "0xa101154", + "bios_version": "1.0", + "bios_revision": "2.19", + "guest_cpu_config": { + "kvm_capabilities": [], + "cpuid_modifiers": [ + { + "leaf": "0x0", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000010000" + }, + { + "register": "ebx", + "bitmap": "0b01101000011101000111010101000001" + }, + { + "register": "ecx", + "bitmap": "0b01000100010011010100000101100011" + }, + { + "register": "edx", + "bitmap": "0b01101001011101000110111001100101" + } + ] + }, + { + "leaf": "0x1", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000101000010000111100010001" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000010000100000000000" + }, + { + "register": "ecx", + "bitmap": "0b11110111111110100011001000000011" + }, + { + "register": "edx", + "bitmap": "0b00000111100010111111101111111111" + } + ] + }, + { + "leaf": "0x2", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x3", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x5", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x6", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000100" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x7", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b11110001101111110000011110101011" + }, + { + "register": "ecx", + "bitmap": "0b00000000010000010101111101001110" + }, + { + "register": "edx", + "bitmap": "0b10001100000000000000000000010000" + } + ] + }, + { + "leaf": "0x7", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000100000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x9", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xa", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xb", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xb", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000101" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000001000000001" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xc", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000001011100111" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000100110001000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000001111" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x2", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x5", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000001000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001101000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x6", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000001000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001110000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x7", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000010000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000010110000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x9", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000001000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000100110000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xe", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xf", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x10", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x40000000", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b01001011010011010101011001001011" + }, + { + "register": "ecx", + "bitmap": "0b01010110010010110100110101010110" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000001001101" + } + ] + }, + { + "leaf": "0x40000001", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000001000000000111111011111011" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000000", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b10000000000000000000000000011111" + }, + { + "register": "ebx", + "bitmap": "0b01101000011101000111010101000001" + }, + { + "register": "ecx", + "bitmap": "0b01000100010011010100000101100011" + }, + { + "register": "edx", + "bitmap": "0b01101001011101000110111001100101" + } + ] + }, + { + "leaf": "0x80000001", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000101000010000111100010001" + }, + { + "register": "ebx", + "bitmap": "0b01000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000110000000000001111110111" + }, + { + "register": "edx", + "bitmap": "0b00101111110100111111101111111111" + } + ] + }, + { + "leaf": "0x80000002", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00100000010001000100110101000001" + }, + { + "register": "ebx", + "bitmap": "0b01000011010110010101000001000101" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000003", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000004", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000005", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b11111111010010001111111101000000" + }, + { + "register": "ebx", + "bitmap": "0b11111111010010001111111101000000" + }, + { + "register": "ecx", + "bitmap": "0b00100000000010000000000101000000" + }, + { + "register": "edx", + "bitmap": "0b00100000000010000000000101000000" + } + ] + }, + { + "leaf": "0x80000006", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01011100000000000010001000000000" + }, + { + "register": "ebx", + "bitmap": "0b01101100000000000100001000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000100000000000110000101000000" + }, + { + "register": "edx", + "bitmap": "0b00001100000000001001000101000000" + } + ] + }, + { + "leaf": "0x80000007", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000100000000" + } + ] + }, + { + "leaf": "0x80000008", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000011100100110100" + }, + { + "register": "ebx", + "bitmap": "0b00000011000000101101001000000101" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000111000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000009", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000a", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000001000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000001001" + } + ] + }, + { + "leaf": "0x8000000b", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000c", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000d", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000e", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000f", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000010", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000011", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000012", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000013", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000014", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000015", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000016", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000017", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000018", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000019", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b11110000010010001111000001000000" + }, + { + "register": "ebx", + "bitmap": "0b11110000010000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001a", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000110" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001b", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001c", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100100001" + }, + { + "register": "ebx", + "bitmap": "0b00000001110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100100010" + }, + { + "register": "ebx", + "bitmap": "0b00000001110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x2", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000101000011" + }, + { + "register": "ebx", + "bitmap": "0b00000001110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000011111111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000010" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x3", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000101100011" + }, + { + "register": "ebx", + "bitmap": "0b00000011110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000111111111111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000001" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x4", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001e", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001f", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000011000011111111111111111011" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000100000110110011" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000001111101110" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000001111101111" + } + ] + } + ], + "msr_modifiers": [ + { + "addr": "0x11", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x12", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x34", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x3b", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x48", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x8b", + "bitmap": "0b0000000000000000000000000000000000000001000000000000000001100101" + }, + { + "addr": "0x9e", + "bitmap": "0b0000000000000000000000000000000000000000000000110000000000000000" + }, + { + "addr": "0xce", + "bitmap": "0b0000000000000000000000000000000010000000000000000000000000000000" + }, + { + "addr": "0x140", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x174", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x175", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x176", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x1a0", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x1fc", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x277", + "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" + }, + { + "addr": "0x4b564d00", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d01", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d02", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d03", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d04", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d05", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x4b564d06", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d07", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000081", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000082", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000083", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000084", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000102", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000103", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0010015", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0010117", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc001011f", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + } + ] + } +} \ No newline at end of file diff --git a/tests/data/cpu_template_helper/fingerprint_AMD_GENOA_6.1host.json b/tests/data/cpu_template_helper/fingerprint_AMD_GENOA_6.1host.json new file mode 100644 index 00000000000..0a096b62831 --- /dev/null +++ b/tests/data/cpu_template_helper/fingerprint_AMD_GENOA_6.1host.json @@ -0,0 +1,1641 @@ +{ + "firecracker_version": "1.12.0-dev", + "kernel_version": "6.1.129-138.220.amzn2023.x86_64", + "microcode_version": "0xa101154", + "bios_version": "1.0", + "bios_revision": "2.19", + "guest_cpu_config": { + "kvm_capabilities": [], + "cpuid_modifiers": [ + { + "leaf": "0x0", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000010000" + }, + { + "register": "ebx", + "bitmap": "0b01101000011101000111010101000001" + }, + { + "register": "ecx", + "bitmap": "0b01000100010011010100000101100011" + }, + { + "register": "edx", + "bitmap": "0b01101001011101000110111001100101" + } + ] + }, + { + "leaf": "0x1", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000101000010000111100010001" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000010000100000000000" + }, + { + "register": "ecx", + "bitmap": "0b11110111111110100011001000000011" + }, + { + "register": "edx", + "bitmap": "0b00000111100010111111101111111111" + } + ] + }, + { + "leaf": "0x2", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x3", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x5", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x6", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000100" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x7", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b11110001101111110000011110101011" + }, + { + "register": "ecx", + "bitmap": "0b00000000010000010101111101001110" + }, + { + "register": "edx", + "bitmap": "0b10001100000000000000000000010000" + } + ] + }, + { + "leaf": "0x7", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000100000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x9", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xa", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xb", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xb", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000101" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000001000000001" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xc", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000001011100111" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000100110001000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000001111" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x2", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x5", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000001000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001101000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x6", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000001000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001110000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x7", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000010000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000010110000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x9", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000001000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000100110000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xe", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xf", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x10", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x40000000", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b01001011010011010101011001001011" + }, + { + "register": "ecx", + "bitmap": "0b01010110010010110100110101010110" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000001001101" + } + ] + }, + { + "leaf": "0x40000001", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000001000000000111111011111011" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000000", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b10000000000000000000000000100001" + }, + { + "register": "ebx", + "bitmap": "0b01101000011101000111010101000001" + }, + { + "register": "ecx", + "bitmap": "0b01000100010011010100000101100011" + }, + { + "register": "edx", + "bitmap": "0b01101001011101000110111001100101" + } + ] + }, + { + "leaf": "0x80000001", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000101000010000111100010001" + }, + { + "register": "ebx", + "bitmap": "0b01000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000110000000000001111110111" + }, + { + "register": "edx", + "bitmap": "0b00101111110100111111101111111111" + } + ] + }, + { + "leaf": "0x80000002", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00100000010001000100110101000001" + }, + { + "register": "ebx", + "bitmap": "0b01000011010110010101000001000101" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000003", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000004", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000005", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b11111111010010001111111101000000" + }, + { + "register": "ebx", + "bitmap": "0b11111111010010001111111101000000" + }, + { + "register": "ecx", + "bitmap": "0b00100000000010000000000101000000" + }, + { + "register": "edx", + "bitmap": "0b00100000000010000000000101000000" + } + ] + }, + { + "leaf": "0x80000006", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01011100000000000010001000000000" + }, + { + "register": "ebx", + "bitmap": "0b01101100000000000100001000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000100000000000110000101000000" + }, + { + "register": "edx", + "bitmap": "0b00001100000000001001000101000000" + } + ] + }, + { + "leaf": "0x80000007", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000100000000" + } + ] + }, + { + "leaf": "0x80000008", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000011100100110100" + }, + { + "register": "ebx", + "bitmap": "0b00010011000000101101001000000101" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000111000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000009", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000a", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000001000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00010000000000011001010000111011" + } + ] + }, + { + "leaf": "0x8000000b", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000c", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000d", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000e", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000000f", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000010", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000011", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000012", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000013", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000014", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000015", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000016", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000017", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000018", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000019", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b11110000010010001111000001000000" + }, + { + "register": "ebx", + "bitmap": "0b11110000010000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001a", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000110" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001b", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001c", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100100001" + }, + { + "register": "ebx", + "bitmap": "0b00000001110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100100010" + }, + { + "register": "ebx", + "bitmap": "0b00000001110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x2", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000101000011" + }, + { + "register": "ebx", + "bitmap": "0b00000001110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000011111111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000010" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x3", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000101100011" + }, + { + "register": "ebx", + "bitmap": "0b00000011110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000111111111111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000001" + } + ] + }, + { + "leaf": "0x8000001d", + "subleaf": "0x4", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001e", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8000001f", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000020", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000021", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000001000101" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + } + ], + "msr_modifiers": [ + { + "addr": "0x11", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x12", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x34", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x3b", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x48", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x8b", + "bitmap": "0b0000000000000000000000000000000000000001000000000000000001100101" + }, + { + "addr": "0x9e", + "bitmap": "0b0000000000000000000000000000000000000000000000110000000000000000" + }, + { + "addr": "0xce", + "bitmap": "0b0000000000000000000000000000000010000000000000000000000000000000" + }, + { + "addr": "0x140", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x174", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x175", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x176", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x1a0", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x1fc", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x277", + "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" + }, + { + "addr": "0x4b564d00", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d01", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d02", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d03", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d04", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d05", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x4b564d06", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d07", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000081", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000082", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000083", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000084", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000102", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000103", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000104", + "bitmap": "0b0000000000000000000000000000000100000000000000000000000000000000" + }, + { + "addr": "0xc0010015", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0010117", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc001011f", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + } + ] + } +} \ No newline at end of file diff --git a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_5.10host.json b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_5.10host.json index 5501ca88ce4..d24260ad7fa 100644 --- a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_5.10host.json @@ -1,9 +1,9 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "5.10.214-202.855.amzn2.x86_64", - "microcode_version": "0xa0011d1", + "firecracker_version": "1.12.0-dev", + "kernel_version": "5.10.234-225.910.amzn2.x86_64", + "microcode_version": "0xa0011db", "bios_version": "1.0", - "bios_revision": "0.69", + "bios_revision": "0.90", "guest_cpu_config": { "kvm_capabilities": [], "cpuid_modifiers": [ @@ -290,7 +290,7 @@ "modifiers": [ { "register": "eax", - "bitmap": "0b00000000000000000000000000000111" + "bitmap": "0b00000000000000000000000000000101" }, { "register": "ebx", diff --git a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_6.1host.json b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_6.1host.json index e0330895de7..5dca6763099 100644 --- a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_6.1host.json @@ -1,9 +1,9 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "6.1.84-99.169.amzn2023.x86_64", - "microcode_version": "0xa0011d1", + "firecracker_version": "1.12.0-dev", + "kernel_version": "6.1.129-138.220.amzn2023.x86_64", + "microcode_version": "0xa0011db", "bios_version": "1.0", - "bios_revision": "0.69", + "bios_revision": "0.90", "guest_cpu_config": { "kvm_capabilities": [], "cpuid_modifiers": [ @@ -290,7 +290,7 @@ "modifiers": [ { "register": "eax", - "bitmap": "0b00000000000000000000000000000111" + "bitmap": "0b00000000000000000000000000000101" }, { "register": "ebx", @@ -543,7 +543,7 @@ "modifiers": [ { "register": "eax", - "bitmap": "0b10000000000000000000000000011111" + "bitmap": "0b10000000000000000000000000100001" }, { "register": "ebx", diff --git a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_N1_5.10host.json b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_N1_5.10host.json index 5bde87f4434..ddf33a21b70 100644 --- a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_N1_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_N1_5.10host.json @@ -1,6 +1,6 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "5.10.214-202.855.amzn2.aarch64", + "firecracker_version": "1.12.0-dev", + "kernel_version": "5.10.234-225.910.amzn2.aarch64", "microcode_version": "0x00000000000000ff", "bios_version": "1.0", "bios_revision": "1.0", @@ -836,6 +836,14 @@ "addr": "0x603000000013df02", "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }, + { + "addr": "0x603000000013df11", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + }, + { + "addr": "0x603000000013df12", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { "addr": "0x603000000013df19", "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" diff --git a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_N1_6.1host.json b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_N1_6.1host.json index 374fcfb2804..7c9f701d409 100644 --- a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_N1_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_N1_6.1host.json @@ -1,6 +1,6 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "6.1.84-99.169.amzn2023.aarch64", + "firecracker_version": "1.12.0-dev", + "kernel_version": "6.1.129-138.220.amzn2023.aarch64", "microcode_version": "0x00000000000000ff", "bios_version": "1.0", "bios_revision": "1.0", @@ -796,6 +796,14 @@ "addr": "0x603000000013df02", "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }, + { + "addr": "0x603000000013df11", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + }, + { + "addr": "0x603000000013df12", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { "addr": "0x603000000013df19", "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" diff --git a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V1_5.10host.json b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V1_5.10host.json index eea613e7749..ef8ca24a9e3 100644 --- a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V1_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V1_5.10host.json @@ -1,6 +1,6 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "5.10.214-202.855.amzn2.aarch64", + "firecracker_version": "1.12.0-dev", + "kernel_version": "5.10.234-225.910.amzn2.aarch64", "microcode_version": "0x0000000000000001", "bios_version": "1.0", "bios_revision": "1.0", @@ -836,6 +836,14 @@ "addr": "0x603000000013df02", "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }, + { + "addr": "0x603000000013df11", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + }, + { + "addr": "0x603000000013df12", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { "addr": "0x603000000013df19", "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" diff --git a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V1_6.1host.json b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V1_6.1host.json index 5be097ea316..b5c7d395709 100644 --- a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V1_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V1_6.1host.json @@ -1,6 +1,6 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "6.1.84-99.169.amzn2023.aarch64", + "firecracker_version": "1.12.0-dev", + "kernel_version": "6.1.129-138.220.amzn2023.aarch64", "microcode_version": "0x0000000000000001", "bios_version": "1.0", "bios_revision": "1.0", @@ -796,6 +796,14 @@ "addr": "0x603000000013df02", "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }, + { + "addr": "0x603000000013df11", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + }, + { + "addr": "0x603000000013df12", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { "addr": "0x603000000013df19", "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" diff --git a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V2_5.10host.json b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V2_5.10host.json new file mode 100644 index 00000000000..89dbb773a09 --- /dev/null +++ b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V2_5.10host.json @@ -0,0 +1,1261 @@ +{ + "firecracker_version": "1.12.0-dev", + "kernel_version": "5.10.234-225.910.amzn2.aarch64", + "microcode_version": "0x0000000000000017", + "bios_version": "1.0", + "bios_revision": "1.0", + "guest_cpu_config": { + "kvm_capabilities": [], + "vcpu_features": [], + "reg_modifiers": [ + { + "addr": "0x60200000001000d4", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60200000001000d5", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6020000000110000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011010" + }, + { + "addr": "0x6020000000110001", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011010" + }, + { + "addr": "0x6020000000110002", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111010" + }, + { + "addr": "0x6030000000100000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000111111000000000000000000000" + }, + { + "addr": "0x6030000000100002", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100004", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100006", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100008", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010000a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010000c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010000e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100010", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100012", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100014", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100016", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100018", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010001a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010001c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010001e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100020", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100022", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100024", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100026", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100028", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010002a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010002c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010002e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100030", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100032", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100034", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100036", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100038", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010003a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010003c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010003e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100042", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111000101" + }, + { + "addr": "0x6030000000100044", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100046", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100048", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010004a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010004c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010004e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100050", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138004", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138005", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138006", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138007", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013800c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013800d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013800e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013800f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138010", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138012", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138014", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138015", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138016", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138017", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013801c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013801d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013801e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013801f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138024", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138025", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138026", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138027", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013802c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013802d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013802e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013802f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138034", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138035", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138036", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138037", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013803c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013803d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013803e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013803f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138044", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138045", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138046", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138047", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013804c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013804d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013804e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013804f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138054", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138055", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138056", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138057", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013805c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013805d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013805e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013805f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138064", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138065", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138066", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138067", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013806c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013806d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013806e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013806f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138074", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138075", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138076", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138077", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013807c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013807d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013807e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013807f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013a038", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000001000011111101010011110001" + }, + { + "addr": "0x603000000013c005", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000" + }, + { + "addr": "0x603000000013c006", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010111" + }, + { + "addr": "0x603000000013c008", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c009", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c010", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c011", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c012", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c013", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c014", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c015", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c016", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c017", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c018", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c019", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c020", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001000100000001000000010001000000100011000100010001000100010001" + }, + { + "addr": "0x603000000013c021", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100001" + }, + { + "addr": "0x603000000013c022", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c023", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c024", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c025", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c026", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c027", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c028", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111001000010000001100000101010000001001" + }, + { + "addr": "0x603000000013c029", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c030", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001001000100001000100000000000100010000001000010010000100100000" + }, + { + "addr": "0x603000000013c031", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000010001000100010001000100000000001000010001000000000010" + }, + { + "addr": "0x603000000013c032", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c033", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c034", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c035", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c036", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c037", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c038", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010001000000000000100000001000100100101" + }, + { + "addr": "0x603000000013c039", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000001100010010000100100010" + }, + { + "addr": "0x603000000013c03a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001001000100001000000010001000100010010000100000001000000010001" + }, + { + "addr": "0x603000000013c03b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c03c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c03d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c03e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c03f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c080", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000110001010000000001111000" + }, + { + "addr": "0x603000000013c081", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c082", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c100", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c101", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c102", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c288", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c289", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c290", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c300", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c3a0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c4f1", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c4f2", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c510", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c518", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c600", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c609", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c681", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c684", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c708", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c801", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000010000000000000000000100011" + }, + { + "addr": "0x603000000013c807", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013d000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013d801", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110100010001001100000000000100" + }, + { + "addr": "0x603000000013dce0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000011101100" + }, + { + "addr": "0x603000000013dce1", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013dce2", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013dce3", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013dce4", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013dce5", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013dce8", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013dcf0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013dcf3", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013de82", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013de83", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df02", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013df11", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + }, + { + "addr": "0x603000000013df12", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013df19", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + }, + { + "addr": "0x603000000013df40", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df41", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df42", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df43", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df44", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df45", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df46", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df47", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df48", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df49", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df4a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df4b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df4c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df4d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df4e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df4f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df50", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df51", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df52", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df53", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df54", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df55", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df56", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df57", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df58", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df59", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df5a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df5b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df5c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df5d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df5e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df60", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df61", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df62", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df63", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df64", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df65", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df66", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df67", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df68", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df69", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df6a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df6b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df6c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df6d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df6e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df6f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df70", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df71", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df72", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df73", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df74", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df75", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df76", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df77", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df78", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df79", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df7a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df7b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df7c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df7d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df7e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df7f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013e180", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013e281", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013e298", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100000000" + }, + { + "addr": "0x6030000000140000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000" + }, + { + "addr": "0x6030000000140001", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010" + }, + { + "addr": "0x6030000000140002", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000140003", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010" + }, + { + "addr": "0x6040000000100054", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100058", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010005c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100060", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100064", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100068", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010006c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100070", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100074", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100078", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010007c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100080", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100084", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100088", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010008c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100090", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100094", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100098", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010009c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000a0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000a4", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000a8", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000ac", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000b0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000b4", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000b8", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000bc", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000c0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000c4", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000c8", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000cc", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000d0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + ] + } +} \ No newline at end of file diff --git a/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V2_6.1host.json b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V2_6.1host.json new file mode 100644 index 00000000000..2e05a08356c --- /dev/null +++ b/tests/data/cpu_template_helper/fingerprint_ARM_NEOVERSE_V2_6.1host.json @@ -0,0 +1,981 @@ +{ + "firecracker_version": "1.12.0-dev", + "kernel_version": "6.1.129-138.220.amzn2023.aarch64", + "microcode_version": "0x0000000000000017", + "bios_version": "1.0", + "bios_revision": "1.0", + "guest_cpu_config": { + "kvm_capabilities": [], + "vcpu_features": [], + "reg_modifiers": [ + { + "addr": "0x60200000001000d4", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60200000001000d5", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6020000000110000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011010" + }, + { + "addr": "0x6020000000110001", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011010" + }, + { + "addr": "0x6020000000110002", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111010" + }, + { + "addr": "0x6030000000100000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000111111000000000000000000000" + }, + { + "addr": "0x6030000000100002", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100004", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100006", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100008", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010000a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010000c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010000e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100010", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100012", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100014", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100016", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100018", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010001a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010001c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010001e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100020", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100022", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100024", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100026", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100028", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010002a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010002c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010002e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100030", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100032", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100034", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100036", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100038", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010003a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010003c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010003e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100042", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111000101" + }, + { + "addr": "0x6030000000100044", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100046", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100048", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010004a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010004c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000010004e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000100050", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138004", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138005", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138006", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138007", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013800c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013800d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013800e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013800f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138010", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138012", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138014", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138015", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138016", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138017", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013801c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013801d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013801e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013801f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138024", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138025", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138026", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138027", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013802c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013802d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013802e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013802f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138034", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138035", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138036", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138037", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013803c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013803d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013803e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013803f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138044", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138045", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138046", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138047", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013804c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013804d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013804e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013804f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138054", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138055", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138056", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138057", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013805c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013805d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013805e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013805f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138064", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138065", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138066", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138067", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013806c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013806d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013806e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013806f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138074", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138075", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138076", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000138077", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013807c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013807d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013807e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013807f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013808c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000" + }, + { + "addr": "0x603000000013a038", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000001000011111101010011110001" + }, + { + "addr": "0x603000000013c005", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000" + }, + { + "addr": "0x603000000013c006", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010111" + }, + { + "addr": "0x603000000013c008", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c009", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c00f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c010", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c011", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c012", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c013", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c014", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c015", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c016", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c017", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c018", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c019", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c01f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c020", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001000100000001000000010001000000100001000100010001000100010001" + }, + { + "addr": "0x603000000013c021", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100001" + }, + { + "addr": "0x603000000013c022", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c023", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c024", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c025", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c026", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c027", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c028", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111000000010000001100000101000000000110" + }, + { + "addr": "0x603000000013c029", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c02f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c030", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001001000100001000100000000000100010000001000010010000100100000" + }, + { + "addr": "0x603000000013c031", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000010001000100010001000100000000001000010001000000000010" + }, + { + "addr": "0x603000000013c032", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c033", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c034", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c035", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c036", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c037", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c038", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010001000000000000100000001000100100101" + }, + { + "addr": "0x603000000013c039", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000001100010010000100100010" + }, + { + "addr": "0x603000000013c03a", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001001000100001000000010001000100010010000000000001000000010001" + }, + { + "addr": "0x603000000013c03b", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c03c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c03d", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c03e", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c03f", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c080", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000110001010000000001111000" + }, + { + "addr": "0x603000000013c081", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c082", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c100", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c101", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c102", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c288", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c289", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c290", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c300", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c3a0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c510", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c518", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c600", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c609", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c681", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c684", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013c708", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013c801", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000010000000000000000000100011" + }, + { + "addr": "0x603000000013c807", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013d000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013d801", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010110100010001001100000000000100" + }, + { + "addr": "0x603000000013de82", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013de83", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013df02", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013df11", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + }, + { + "addr": "0x603000000013df12", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x603000000013df19", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + }, + { + "addr": "0x603000000013e180", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013e281", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000001110111100111111011000111111011011011101011011100000011011110" + }, + { + "addr": "0x603000000013e298", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100000000" + }, + { + "addr": "0x6030000000140000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000001" + }, + { + "addr": "0x6030000000140001", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010" + }, + { + "addr": "0x6030000000140002", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6030000000140003", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010" + }, + { + "addr": "0x6030000000160000", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x6030000000160001", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x6030000000160002", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011" + }, + { + "addr": "0x6040000000100054", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100058", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010005c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100060", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100064", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100068", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010006c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100070", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100074", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100078", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010007c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100080", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100084", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100088", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010008c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100090", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100094", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x6040000000100098", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x604000000010009c", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000a0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000a4", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000a8", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000ac", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000b0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000b4", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000b8", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000bc", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000c0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000c4", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000c8", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000cc", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x60400000001000d0", + "bitmap": "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + ] + } +} \ No newline at end of file diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_5.10host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_5.10host.json index 8568992dcef..45e3d7ed4b8 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_5.10host.json @@ -1,9 +1,9 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "5.10.215-203.850.amzn2.x86_64", - "microcode_version": "0x5003604", + "firecracker_version": "1.12.0-dev", + "kernel_version": "5.10.234-225.910.amzn2.x86_64", + "microcode_version": "0x5003801", "bios_version": "1.0", - "bios_revision": "3.80", + "bios_revision": "4.11", "guest_cpu_config": { "kvm_capabilities": [], "cpuid_modifiers": [ @@ -382,7 +382,7 @@ "modifiers": [ { "register": "eax", - "bitmap": "0b00000000000000000000000000000111" + "bitmap": "0b00000000000000000000000000000101" }, { "register": "ebx", diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_6.1host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_6.1host.json index fc9ee8dddf8..1dc7abee252 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_6.1host.json @@ -1,9 +1,9 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "6.1.84-99.169.amzn2023.x86_64", - "microcode_version": "0x5003604", + "firecracker_version": "1.12.0-dev", + "kernel_version": "6.1.129-138.220.amzn2023.x86_64", + "microcode_version": "0x5003801", "bios_version": "1.0", - "bios_revision": "3.80", + "bios_revision": "4.11", "guest_cpu_config": { "kvm_capabilities": [], "cpuid_modifiers": [ @@ -382,7 +382,7 @@ "modifiers": [ { "register": "eax", - "bitmap": "0b00000000000000000000000000000111" + "bitmap": "0b00000000000000000000000000000101" }, { "register": "ebx", diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_5.10host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_5.10host.json index 3a6c8d5123e..5bf57d51da9 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_5.10host.json @@ -1,9 +1,9 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "5.10.215-203.850.amzn2.x86_64", - "microcode_version": "0xd0003b9", + "firecracker_version": "1.12.0-dev", + "kernel_version": "5.10.234-225.910.amzn2.x86_64", + "microcode_version": "0xd0003f6", "bios_version": "1.0", - "bios_revision": "1.36", + "bios_revision": "1.40", "guest_cpu_config": { "kvm_capabilities": [], "cpuid_modifiers": [ @@ -405,7 +405,7 @@ "modifiers": [ { "register": "eax", - "bitmap": "0b00000000000000000000000000000111" + "bitmap": "0b00000000000000000000000000000101" }, { "register": "ebx", diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_6.1host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_6.1host.json index d307c491d2a..05dea2e484a 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_6.1host.json @@ -1,9 +1,9 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "6.1.84-99.169.amzn2023.x86_64", - "microcode_version": "0xd0003b9", + "firecracker_version": "1.12.0-dev", + "kernel_version": "6.1.129-138.220.amzn2023.x86_64", + "microcode_version": "0xd0003f6", "bios_version": "1.0", - "bios_revision": "1.36", + "bios_revision": "1.40", "guest_cpu_config": { "kvm_capabilities": [], "cpuid_modifiers": [ @@ -428,7 +428,7 @@ "modifiers": [ { "register": "eax", - "bitmap": "0b00000000000000000000000000000111" + "bitmap": "0b00000000000000000000000000000101" }, { "register": "ebx", diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_SAPPHIRE_RAPIDS_5.10host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_SAPPHIRE_RAPIDS_5.10host.json new file mode 100644 index 00000000000..67a3d76b1ab --- /dev/null +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_SAPPHIRE_RAPIDS_5.10host.json @@ -0,0 +1,1434 @@ +{ + "firecracker_version": "1.12.0-dev", + "kernel_version": "5.10.234-225.910.amzn2.x86_64", + "microcode_version": "0x2b000620", + "bios_version": "1.0", + "bios_revision": "3.3", + "guest_cpu_config": { + "kvm_capabilities": [], + "cpuid_modifiers": [ + { + "leaf": "0x0", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000011111" + }, + { + "register": "ebx", + "bitmap": "0b01110101011011100110010101000111" + }, + { + "register": "ecx", + "bitmap": "0b01101100011001010111010001101110" + }, + { + "register": "edx", + "bitmap": "0b01001001011001010110111001101001" + } + ] + }, + { + "leaf": "0x1", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000010000000011011111000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000010000100000000000" + }, + { + "register": "ecx", + "bitmap": "0b11110111111110100011001000100011" + }, + { + "register": "edx", + "bitmap": "0b00001111100010111111101111111111" + } + ] + }, + { + "leaf": "0x2", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000111111101111111100000001" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000011110000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x3", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100100001" + }, + { + "register": "ebx", + "bitmap": "0b00000010110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100100010" + }, + { + "register": "ebx", + "bitmap": "0b00000001110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x2", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000101000011" + }, + { + "register": "ebx", + "bitmap": "0b00000011110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000011111111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x3", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000101100011" + }, + { + "register": "ebx", + "bitmap": "0b00000011100000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000011011111111111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000100" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x4", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x5", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x6", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000100" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x7", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b11110001101111110010011111101011" + }, + { + "register": "ecx", + "bitmap": "0b00011010010000010101111101001110" + }, + { + "register": "edx", + "bitmap": "0b10101100000000010100010000010000" + } + ] + }, + { + "leaf": "0x7", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000100000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x8", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x9", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xa", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xb", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xb", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000101" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000001000000001" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xc", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000001011100111" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000101010001000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000001111" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x2", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x5", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000001000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000010001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x6", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000001000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000010010000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x7", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000010000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000011010000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x9", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000001000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000101010000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xe", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xf", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x10", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x11", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x12", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x13", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x14", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x15", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x16", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x17", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x18", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x19", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1a", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1b", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1c", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1d", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1e", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1f", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1f", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000101" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000001000000001" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x40000000", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b01001011010011010101011001001011" + }, + { + "register": "ecx", + "bitmap": "0b01010110010010110100110101010110" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000001001101" + } + ] + }, + { + "leaf": "0x40000001", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000001000000000111111011111011" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000000", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b10000000000000000000000000001000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000001", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000100100001" + }, + { + "register": "edx", + "bitmap": "0b00101100000100000000100000000000" + } + ] + }, + { + "leaf": "0x80000002", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01100101011101000110111001001001" + }, + { + "register": "ebx", + "bitmap": "0b00101001010100100010100001101100" + }, + { + "register": "ecx", + "bitmap": "0b01101111011001010101100000100000" + }, + { + "register": "edx", + "bitmap": "0b00101001010100100010100001101110" + } + ] + }, + { + "leaf": "0x80000003", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01101111011100100101000000100000" + }, + { + "register": "ebx", + "bitmap": "0b01110011011100110110010101100011" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000111001001101111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000004", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000005", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000006", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00001000000000000111000001000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000007", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000100000000" + } + ] + }, + { + "leaf": "0x80000008", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000011100100101110" + }, + { + "register": "ebx", + "bitmap": "0b00000001000000001101001000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + } + ], + "msr_modifiers": [ + { + "addr": "0x11", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x12", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x34", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x3a", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x3b", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x48", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x8b", + "bitmap": "0b0000000000000000000000000000000100000000000000000000000000000000" + }, + { + "addr": "0x9e", + "bitmap": "0b0000000000000000000000000000000000000000000000110000000000000000" + }, + { + "addr": "0xce", + "bitmap": "0b0000000000000000000000000000000010000000000000000000000000000000" + }, + { + "addr": "0xe1", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x10a", + "bitmap": "0b0000000000000000000000000000000000001100000010001110000011101011" + }, + { + "addr": "0x140", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x174", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x175", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x176", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x1a0", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x1fc", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x277", + "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" + }, + { + "addr": "0x4b564d00", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d01", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d02", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d03", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d04", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d05", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x4b564d06", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d07", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000081", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000082", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000083", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000084", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000102", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000103", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0010015", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + } + ] + } +} \ No newline at end of file diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_SAPPHIRE_RAPIDS_6.1host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_SAPPHIRE_RAPIDS_6.1host.json new file mode 100644 index 00000000000..38855ec24cc --- /dev/null +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_SAPPHIRE_RAPIDS_6.1host.json @@ -0,0 +1,1534 @@ +{ + "firecracker_version": "1.12.0-dev", + "kernel_version": "6.1.129-138.220.amzn2023.x86_64", + "microcode_version": "0x2b000620", + "bios_version": "1.0", + "bios_revision": "3.3", + "guest_cpu_config": { + "kvm_capabilities": [], + "cpuid_modifiers": [ + { + "leaf": "0x0", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000011111" + }, + { + "register": "ebx", + "bitmap": "0b01110101011011100110010101000111" + }, + { + "register": "ecx", + "bitmap": "0b01101100011001010111010001101110" + }, + { + "register": "edx", + "bitmap": "0b01001001011001010110111001101001" + } + ] + }, + { + "leaf": "0x1", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000010000000011011111000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000010000100000000000" + }, + { + "register": "ecx", + "bitmap": "0b11110111111110100011001000100011" + }, + { + "register": "edx", + "bitmap": "0b00001111100010111111101111111111" + } + ] + }, + { + "leaf": "0x2", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000111111101111111100000001" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000011110000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x3", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100100001" + }, + { + "register": "ebx", + "bitmap": "0b00000010110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100100010" + }, + { + "register": "ebx", + "bitmap": "0b00000001110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x2", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000101000011" + }, + { + "register": "ebx", + "bitmap": "0b00000011110000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000011111111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x3", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000101100011" + }, + { + "register": "ebx", + "bitmap": "0b00000011100000000000000000111111" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000011011111111111111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000100" + } + ] + }, + { + "leaf": "0x4", + "subleaf": "0x4", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x5", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x6", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000100" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x7", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000010" + }, + { + "register": "ebx", + "bitmap": "0b11110001101111110010011111101011" + }, + { + "register": "ecx", + "bitmap": "0b00011011010000010101111101001110" + }, + { + "register": "edx", + "bitmap": "0b10101111110000010100010000010000" + } + ] + }, + { + "leaf": "0x7", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000110000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x7", + "subleaf": "0x2", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000010111" + } + ] + }, + { + "leaf": "0x8", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x9", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xa", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xb", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xb", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000101" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000001000000001" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xc", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000001100000001011100111" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000010101100000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000011111" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x2", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000001001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x5", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000001000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000010001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x6", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000001000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000010010000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x7", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000010000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000011010000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x9", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000001000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000101010000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x11", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000001000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000101011000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000010" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xd", + "subleaf": "0x12", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000010000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000101100000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000110" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xe", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0xf", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x10", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x11", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x12", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x13", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x14", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x15", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x16", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x17", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x18", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x19", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1a", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1b", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1c", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1d", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1d", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000100000000000010000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000010000000000001000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000010000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1e", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000100000000010000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1f", + "subleaf": "0x0", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000100000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x1f", + "subleaf": "0x1", + "flags": 1, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000101" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000001" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000001000000001" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x40000000", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01000000000000000000000000000001" + }, + { + "register": "ebx", + "bitmap": "0b01001011010011010101011001001011" + }, + { + "register": "ecx", + "bitmap": "0b01010110010010110100110101010110" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000001001101" + } + ] + }, + { + "leaf": "0x40000001", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000001000000000111111011111011" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000000", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b10000000000000000000000000001000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000001", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000100100001" + }, + { + "register": "edx", + "bitmap": "0b00101100000100000000100000000000" + } + ] + }, + { + "leaf": "0x80000002", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01100101011101000110111001001001" + }, + { + "register": "ebx", + "bitmap": "0b00101001010100100010100001101100" + }, + { + "register": "ecx", + "bitmap": "0b01101111011001010101100000100000" + }, + { + "register": "edx", + "bitmap": "0b00101001010100100010100001101110" + } + ] + }, + { + "leaf": "0x80000003", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b01101111011100100101000000100000" + }, + { + "register": "ebx", + "bitmap": "0b01110011011100110110010101100011" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000111001001101111" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000004", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000005", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000006", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00001000000000000111000001000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + }, + { + "leaf": "0x80000007", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ebx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000100000000" + } + ] + }, + { + "leaf": "0x80000008", + "subleaf": "0x0", + "flags": 0, + "modifiers": [ + { + "register": "eax", + "bitmap": "0b00000000000000000011100100101110" + }, + { + "register": "ebx", + "bitmap": "0b00000001000000001101001000000000" + }, + { + "register": "ecx", + "bitmap": "0b00000000000000000000000000000000" + }, + { + "register": "edx", + "bitmap": "0b00000000000000000000000000000000" + } + ] + } + ], + "msr_modifiers": [ + { + "addr": "0x11", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x12", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x34", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x3a", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x3b", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x48", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x8b", + "bitmap": "0b0000000000000000000000000000000100000000000000000000000000000000" + }, + { + "addr": "0x9e", + "bitmap": "0b0000000000000000000000000000000000000000000000110000000000000000" + }, + { + "addr": "0xce", + "bitmap": "0b0000000000000000000000000000000010000000000000000000000000000000" + }, + { + "addr": "0xe1", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x10a", + "bitmap": "0b0000000000000000000000000000000000001100000010001110000011101011" + }, + { + "addr": "0x140", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x174", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x175", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x176", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x1a0", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x1c4", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x1c5", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x1fc", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x277", + "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" + }, + { + "addr": "0x4b564d00", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d01", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d02", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d03", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d04", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d05", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "addr": "0x4b564d06", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0x4b564d07", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000081", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000082", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000083", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000084", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000102", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0000103", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "addr": "0xc0010015", + "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" + } + ] + } +} \ No newline at end of file diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_5.10host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_5.10host.json index c80924adefa..d6eb7d0a478 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_5.10host.json @@ -1,9 +1,9 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "5.10.215-203.850.amzn2.x86_64", + "firecracker_version": "1.12.0-dev", + "kernel_version": "5.10.234-225.910.amzn2.x86_64", "microcode_version": "0x2007006", "bios_version": "1.0", - "bios_revision": "3.80", + "bios_revision": "4.11", "guest_cpu_config": { "kvm_capabilities": [], "cpuid_modifiers": [ @@ -382,7 +382,7 @@ "modifiers": [ { "register": "eax", - "bitmap": "0b00000000000000000000000000000111" + "bitmap": "0b00000000000000000000000000000101" }, { "register": "ebx", diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_6.1host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_6.1host.json index 324619e3268..432a97f64bc 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_6.1host.json @@ -1,9 +1,9 @@ { - "firecracker_version": "1.8.0-dev", - "kernel_version": "6.1.84-99.169.amzn2023.x86_64", + "firecracker_version": "1.12.0-dev", + "kernel_version": "6.1.129-138.220.amzn2023.x86_64", "microcode_version": "0x2007006", "bios_version": "1.0", - "bios_revision": "3.80", + "bios_revision": "4.11", "guest_cpu_config": { "kvm_capabilities": [], "cpuid_modifiers": [ @@ -382,7 +382,7 @@ "modifiers": [ { "register": "eax", - "bitmap": "0b00000000000000000000000000000111" + "bitmap": "0b00000000000000000000000000000101" }, { "register": "ebx", diff --git a/tests/data/msr/msr_list_T2A_AMD_GENOA_5.10host_5.10guest.csv b/tests/data/msr/msr_list_T2A_AMD_GENOA_5.10host_5.10guest.csv new file mode 100644 index 00000000000..82f8d2673d7 --- /dev/null +++ b/tests/data/msr/msr_list_T2A_AMD_GENOA_5.10host_5.10guest.csv @@ -0,0 +1,298 @@ +MSR_ADDR,VALUE +0,0x0 +0x1,0x0 +0x10,0x86e8ca1e +0x11,0x24a1008 +0x12,0x24a2001 +0x17,0x0 +0x1b,0xfee00d00 +0x2a,0x0 +0x2c,0x1000000 +0x34,0x0 +0x3b,0x0 +0x48,0x0 +0x8b,0x1000065 +0xc1,0x0 +0xc2,0x0 +0xcd,0x3 +0xce,0x80000000 +0xfe,0x508 +0x11e,0xbe702111 +0x140,0x0 +0x174,0x10 +0x175,0xfffffe0000003000 +0x176,0xffffffff81a01510 +0x179,0x20 +0x17a,0x0 +0x186,0x0 +0x187,0x0 +0x198,0x400000003e8 +0x199,0x0 +0x1a0,0x1 +0x1d9,0x0 +0x1db,0x0 +0x1dc,0x0 +0x1dd,0x0 +0x1de,0x0 +0x1fc,0x0 +0x200,0x0 +0x201,0x0 +0x202,0x0 +0x203,0x0 +0x204,0x0 +0x205,0x0 +0x206,0x0 +0x207,0x0 +0x208,0x0 +0x209,0x0 +0x20a,0x0 +0x20b,0x0 +0x20c,0x0 +0x20d,0x0 +0x20e,0x0 +0x20f,0x0 +0x250,0x0 +0x258,0x0 +0x259,0x0 +0x268,0x0 +0x269,0x0 +0x26a,0x0 +0x26b,0x0 +0x26c,0x0 +0x26d,0x0 +0x26e,0x0 +0x26f,0x0 +0x277,0x7040600070406 +0x2ff,0x0 +0x400,0x0 +0x401,0x0 +0x402,0x0 +0x403,0x0 +0x404,0x0 +0x405,0x0 +0x406,0x0 +0x407,0x0 +0x408,0x0 +0x409,0x0 +0x40a,0x0 +0x40b,0x0 +0x40c,0x0 +0x40d,0x0 +0x40e,0x0 +0x40f,0x0 +0x410,0x0 +0x411,0x0 +0x412,0x0 +0x413,0x0 +0x414,0x0 +0x415,0x0 +0x416,0x0 +0x417,0x0 +0x418,0x0 +0x419,0x0 +0x41a,0x0 +0x41b,0x0 +0x41c,0x0 +0x41d,0x0 +0x41e,0x0 +0x41f,0x0 +0x420,0x0 +0x421,0x0 +0x422,0x0 +0x423,0x0 +0x424,0x0 +0x425,0x0 +0x426,0x0 +0x427,0x0 +0x428,0x0 +0x429,0x0 +0x42a,0x0 +0x42b,0x0 +0x42c,0x0 +0x42d,0x0 +0x42e,0x0 +0x42f,0x0 +0x430,0x0 +0x431,0x0 +0x432,0x0 +0x433,0x0 +0x434,0x0 +0x435,0x0 +0x436,0x0 +0x437,0x0 +0x438,0x0 +0x439,0x0 +0x43a,0x0 +0x43b,0x0 +0x43c,0x0 +0x43d,0x0 +0x43e,0x0 +0x43f,0x0 +0x440,0x0 +0x441,0x0 +0x442,0x0 +0x443,0x0 +0x444,0x0 +0x445,0x0 +0x446,0x0 +0x447,0x0 +0x448,0x0 +0x449,0x0 +0x44a,0x0 +0x44b,0x0 +0x44c,0x0 +0x44d,0x0 +0x44e,0x0 +0x44f,0x0 +0x450,0x0 +0x451,0x0 +0x452,0x0 +0x453,0x0 +0x454,0x0 +0x455,0x0 +0x456,0x0 +0x457,0x0 +0x458,0x0 +0x459,0x0 +0x45a,0x0 +0x45b,0x0 +0x45c,0x0 +0x45d,0x0 +0x45e,0x0 +0x45f,0x0 +0x460,0x0 +0x461,0x0 +0x462,0x0 +0x463,0x0 +0x464,0x0 +0x465,0x0 +0x466,0x0 +0x467,0x0 +0x468,0x0 +0x469,0x0 +0x46a,0x0 +0x46b,0x0 +0x46c,0x0 +0x46d,0x0 +0x46e,0x0 +0x46f,0x0 +0x470,0x0 +0x471,0x0 +0x472,0x0 +0x473,0x0 +0x474,0x0 +0x475,0x0 +0x476,0x0 +0x477,0x0 +0x478,0x0 +0x479,0x0 +0x47a,0x0 +0x47b,0x0 +0x47c,0x0 +0x47d,0x0 +0x47e,0x0 +0x47f,0x0 +0x606,0x0 +0x611,0x0 +0x619,0x0 +0x639,0x0 +0x641,0x0 +0x6e0,0x150b49126 +0x802,0x0 +0x803,0x50014 +0x808,0x10 +0x80a,0x10 +0x80d,0x1 +0x80f,0x1ff +0x810,0x0 +0x811,0x0 +0x812,0x0 +0x813,0x0 +0x814,0x0 +0x815,0x0 +0x816,0x0 +0x817,0x0 +0x818,0x0 +0x819,0x0 +0x81a,0x0 +0x81b,0x0 +0x81c,0x0 +0x81d,0x0 +0x81e,0x0 +0x81f,0x0 +0x820,0x0 +0x821,0x0 +0x822,0x0 +0x823,0x0 +0x824,0x0 +0x825,0x0 +0x826,0x0 +0x827,0x0 +0x828,0x0 +0x830,0x0 +0x832,0x400ec +0x833,0x10000 +0x834,0x400 +0x835,0x10700 +0x836,0x400 +0x837,0xfe +0x838,0x0 +0x839,0x0 +0x83e,0x0 +0xc0000080,0xd01 +0xc0000081,0x23001000000000 +0xc0000082,0xffffffff81a00080 +0xc0000083,0xffffffff81a015c0 +0xc0000084,0x47700 +0xc0000100,0x7fbe30c03740 +0xc0000101,0xffff88803ec00000 +0xc0000102,0x0 +0xc0000103,0x0 +0xc0010000,0x0 +0xc0010001,0x0 +0xc0010002,0x0 +0xc0010003,0x0 +0xc0010004,0x0 +0xc0010005,0x0 +0xc0010006,0x0 +0xc0010007,0x0 +0xc0010010,0x0 +0xc0010015,0x0 +0xc001001b,0x20000000 +0xc001001f,0x0 +0xc0010055,0x0 +0xc0010058,0x0 +0xc0010112,0x0 +0xc0010113,0x0 +0xc0010114,0x0 +0xc0010117,0x0 +0xc001011f,0x0 +0xc0010131,0x0 +0xc0010140,0x4 +0xc0010141,0x0 +0xc0010200,0x0 +0xc0010201,0x0 +0xc0010202,0x0 +0xc0010203,0x0 +0xc0010204,0x0 +0xc0010205,0x0 +0xc0010206,0x0 +0xc0010207,0x0 +0xc0010208,0x0 +0xc0010209,0x0 +0xc001020a,0x0 +0xc001020b,0xffff +0xc0011021,0x0 +0xc0011022,0x0 +0xc0011023,0x0 +0xc0011029,0x2 +0xc001102a,0x0 +0xc001102c,0x0 +0x400000000,0x0 +0x2000000000,0x0 +0x4000000000,0x0 +0x8000000000,0x0 +0x1000000000000,0x0 +0x3c000000000000,0x0 +0x80000000000000,0x0 +0x40000000000000,0x0 diff --git a/tests/data/msr/msr_list_T2A_AMD_GENOA_5.10host_6.1guest.csv b/tests/data/msr/msr_list_T2A_AMD_GENOA_5.10host_6.1guest.csv new file mode 100644 index 00000000000..cbb1af0b3ac --- /dev/null +++ b/tests/data/msr/msr_list_T2A_AMD_GENOA_5.10host_6.1guest.csv @@ -0,0 +1,298 @@ +MSR_ADDR,VALUE +0,0x0 +0x1,0x0 +0x10,0x7cc4d24c +0x11,0x25cb008 +0x12,0x25cc001 +0x17,0x0 +0x1b,0xfee00d00 +0x2a,0x0 +0x2c,0x1000000 +0x34,0x0 +0x3b,0x0 +0x48,0x0 +0x8b,0x1000065 +0xc1,0x0 +0xc2,0x0 +0xcd,0x3 +0xce,0x80000000 +0xfe,0x508 +0x11e,0xbe702111 +0x140,0x0 +0x174,0x10 +0x175,0xfffffe0000003000 +0x176,0xffffffff81a01620 +0x179,0x20 +0x17a,0x0 +0x186,0x0 +0x187,0x0 +0x198,0x400000003e8 +0x199,0x0 +0x1a0,0x1 +0x1d9,0x0 +0x1db,0x0 +0x1dc,0x0 +0x1dd,0x0 +0x1de,0x0 +0x1fc,0x0 +0x200,0x0 +0x201,0x0 +0x202,0x0 +0x203,0x0 +0x204,0x0 +0x205,0x0 +0x206,0x0 +0x207,0x0 +0x208,0x0 +0x209,0x0 +0x20a,0x0 +0x20b,0x0 +0x20c,0x0 +0x20d,0x0 +0x20e,0x0 +0x20f,0x0 +0x250,0x0 +0x258,0x0 +0x259,0x0 +0x268,0x0 +0x269,0x0 +0x26a,0x0 +0x26b,0x0 +0x26c,0x0 +0x26d,0x0 +0x26e,0x0 +0x26f,0x0 +0x277,0x7040600070406 +0x2ff,0x0 +0x400,0x0 +0x401,0x0 +0x402,0x0 +0x403,0x0 +0x404,0x0 +0x405,0x0 +0x406,0x0 +0x407,0x0 +0x408,0x0 +0x409,0x0 +0x40a,0x0 +0x40b,0x0 +0x40c,0x0 +0x40d,0x0 +0x40e,0x0 +0x40f,0x0 +0x410,0x0 +0x411,0x0 +0x412,0x0 +0x413,0x0 +0x414,0x0 +0x415,0x0 +0x416,0x0 +0x417,0x0 +0x418,0x0 +0x419,0x0 +0x41a,0x0 +0x41b,0x0 +0x41c,0x0 +0x41d,0x0 +0x41e,0x0 +0x41f,0x0 +0x420,0x0 +0x421,0x0 +0x422,0x0 +0x423,0x0 +0x424,0x0 +0x425,0x0 +0x426,0x0 +0x427,0x0 +0x428,0x0 +0x429,0x0 +0x42a,0x0 +0x42b,0x0 +0x42c,0x0 +0x42d,0x0 +0x42e,0x0 +0x42f,0x0 +0x430,0x0 +0x431,0x0 +0x432,0x0 +0x433,0x0 +0x434,0x0 +0x435,0x0 +0x436,0x0 +0x437,0x0 +0x438,0x0 +0x439,0x0 +0x43a,0x0 +0x43b,0x0 +0x43c,0x0 +0x43d,0x0 +0x43e,0x0 +0x43f,0x0 +0x440,0x0 +0x441,0x0 +0x442,0x0 +0x443,0x0 +0x444,0x0 +0x445,0x0 +0x446,0x0 +0x447,0x0 +0x448,0x0 +0x449,0x0 +0x44a,0x0 +0x44b,0x0 +0x44c,0x0 +0x44d,0x0 +0x44e,0x0 +0x44f,0x0 +0x450,0x0 +0x451,0x0 +0x452,0x0 +0x453,0x0 +0x454,0x0 +0x455,0x0 +0x456,0x0 +0x457,0x0 +0x458,0x0 +0x459,0x0 +0x45a,0x0 +0x45b,0x0 +0x45c,0x0 +0x45d,0x0 +0x45e,0x0 +0x45f,0x0 +0x460,0x0 +0x461,0x0 +0x462,0x0 +0x463,0x0 +0x464,0x0 +0x465,0x0 +0x466,0x0 +0x467,0x0 +0x468,0x0 +0x469,0x0 +0x46a,0x0 +0x46b,0x0 +0x46c,0x0 +0x46d,0x0 +0x46e,0x0 +0x46f,0x0 +0x470,0x0 +0x471,0x0 +0x472,0x0 +0x473,0x0 +0x474,0x0 +0x475,0x0 +0x476,0x0 +0x477,0x0 +0x478,0x0 +0x479,0x0 +0x47a,0x0 +0x47b,0x0 +0x47c,0x0 +0x47d,0x0 +0x47e,0x0 +0x47f,0x0 +0x606,0x0 +0x611,0x0 +0x619,0x0 +0x639,0x0 +0x641,0x0 +0x6e0,0x13998e6da +0x802,0x0 +0x803,0x50014 +0x808,0x10 +0x80a,0x10 +0x80d,0x1 +0x80f,0x1ff +0x810,0x0 +0x811,0x0 +0x812,0x0 +0x813,0x0 +0x814,0x0 +0x815,0x0 +0x816,0x0 +0x817,0x0 +0x818,0x0 +0x819,0x0 +0x81a,0x0 +0x81b,0x0 +0x81c,0x0 +0x81d,0x0 +0x81e,0x0 +0x81f,0x0 +0x820,0x0 +0x821,0x0 +0x822,0x0 +0x823,0x0 +0x824,0x0 +0x825,0x0 +0x826,0x0 +0x827,0x0 +0x828,0x0 +0x830,0x0 +0x832,0x400ec +0x833,0x10000 +0x834,0x400 +0x835,0x10700 +0x836,0x400 +0x837,0xfe +0x838,0x0 +0x839,0x0 +0x83e,0x0 +0xc0000080,0xd01 +0xc0000081,0x23001000000000 +0xc0000082,0xffffffff81a00080 +0xc0000083,0xffffffff81a016e0 +0xc0000084,0x257fd5 +0xc0000100,0x7f2345db1740 +0xc0000101,0xffff88803ec00000 +0xc0000102,0x0 +0xc0000103,0x0 +0xc0010000,0x0 +0xc0010001,0x0 +0xc0010002,0x0 +0xc0010003,0x0 +0xc0010004,0x0 +0xc0010005,0x0 +0xc0010006,0x0 +0xc0010007,0x0 +0xc0010010,0x0 +0xc0010015,0x0 +0xc001001b,0x20000000 +0xc001001f,0x0 +0xc0010055,0x0 +0xc0010058,0x0 +0xc0010112,0x0 +0xc0010113,0x0 +0xc0010114,0x0 +0xc0010117,0x0 +0xc001011f,0x0 +0xc0010131,0x0 +0xc0010140,0x4 +0xc0010141,0x0 +0xc0010200,0x0 +0xc0010201,0x0 +0xc0010202,0x0 +0xc0010203,0x0 +0xc0010204,0x0 +0xc0010205,0x0 +0xc0010206,0x0 +0xc0010207,0x0 +0xc0010208,0x0 +0xc0010209,0x0 +0xc001020a,0x0 +0xc001020b,0xffff +0xc0011021,0x0 +0xc0011022,0x0 +0xc0011023,0x0 +0xc0011029,0x2 +0xc001102a,0x0 +0xc001102c,0x0 +0x400000000,0x0 +0x2000000000,0x0 +0x4000000000,0x0 +0x8000000000,0x0 +0x1000000000000,0x0 +0x3c000000000000,0x0 +0x80000000000000,0x0 +0x40000000000000,0x0 diff --git a/tests/data/msr/msr_list_T2A_AMD_GENOA_6.1host_5.10guest.csv b/tests/data/msr/msr_list_T2A_AMD_GENOA_6.1host_5.10guest.csv new file mode 100644 index 00000000000..b3015830e3f --- /dev/null +++ b/tests/data/msr/msr_list_T2A_AMD_GENOA_6.1host_5.10guest.csv @@ -0,0 +1,300 @@ +MSR_ADDR,VALUE +0,0x0 +0x1,0x0 +0x10,0x916c8f40 +0x11,0x24a1008 +0x12,0x24a2001 +0x17,0x0 +0x1b,0xfee00d00 +0x2a,0x0 +0x2c,0x1000000 +0x34,0x0 +0x3b,0x0 +0x48,0x0 +0x8b,0x1000065 +0xc1,0x0 +0xc2,0x0 +0xcd,0x3 +0xce,0x80000000 +0xfe,0x508 +0x11e,0xbe702111 +0x140,0x0 +0x174,0x10 +0x175,0x3000 +0x176,0x81a01510 +0x179,0x20 +0x17a,0x0 +0x186,0x0 +0x187,0x0 +0x198,0x400000003e8 +0x199,0x0 +0x1a0,0x1 +0x1d9,0x0 +0x1db,0x0 +0x1dc,0x0 +0x1dd,0x0 +0x1de,0x0 +0x1fc,0x0 +0x200,0x0 +0x201,0x0 +0x202,0x0 +0x203,0x0 +0x204,0x0 +0x205,0x0 +0x206,0x0 +0x207,0x0 +0x208,0x0 +0x209,0x0 +0x20a,0x0 +0x20b,0x0 +0x20c,0x0 +0x20d,0x0 +0x20e,0x0 +0x20f,0x0 +0x250,0x0 +0x258,0x0 +0x259,0x0 +0x268,0x0 +0x269,0x0 +0x26a,0x0 +0x26b,0x0 +0x26c,0x0 +0x26d,0x0 +0x26e,0x0 +0x26f,0x0 +0x277,0x7040600070406 +0x2ff,0x0 +0x400,0x0 +0x401,0x0 +0x402,0x0 +0x403,0x0 +0x404,0x0 +0x405,0x0 +0x406,0x0 +0x407,0x0 +0x408,0x0 +0x409,0x0 +0x40a,0x0 +0x40b,0x0 +0x40c,0x0 +0x40d,0x0 +0x40e,0x0 +0x40f,0x0 +0x410,0x0 +0x411,0x0 +0x412,0x0 +0x413,0x0 +0x414,0x0 +0x415,0x0 +0x416,0x0 +0x417,0x0 +0x418,0x0 +0x419,0x0 +0x41a,0x0 +0x41b,0x0 +0x41c,0x0 +0x41d,0x0 +0x41e,0x0 +0x41f,0x0 +0x420,0x0 +0x421,0x0 +0x422,0x0 +0x423,0x0 +0x424,0x0 +0x425,0x0 +0x426,0x0 +0x427,0x0 +0x428,0x0 +0x429,0x0 +0x42a,0x0 +0x42b,0x0 +0x42c,0x0 +0x42d,0x0 +0x42e,0x0 +0x42f,0x0 +0x430,0x0 +0x431,0x0 +0x432,0x0 +0x433,0x0 +0x434,0x0 +0x435,0x0 +0x436,0x0 +0x437,0x0 +0x438,0x0 +0x439,0x0 +0x43a,0x0 +0x43b,0x0 +0x43c,0x0 +0x43d,0x0 +0x43e,0x0 +0x43f,0x0 +0x440,0x0 +0x441,0x0 +0x442,0x0 +0x443,0x0 +0x444,0x0 +0x445,0x0 +0x446,0x0 +0x447,0x0 +0x448,0x0 +0x449,0x0 +0x44a,0x0 +0x44b,0x0 +0x44c,0x0 +0x44d,0x0 +0x44e,0x0 +0x44f,0x0 +0x450,0x0 +0x451,0x0 +0x452,0x0 +0x453,0x0 +0x454,0x0 +0x455,0x0 +0x456,0x0 +0x457,0x0 +0x458,0x0 +0x459,0x0 +0x45a,0x0 +0x45b,0x0 +0x45c,0x0 +0x45d,0x0 +0x45e,0x0 +0x45f,0x0 +0x460,0x0 +0x461,0x0 +0x462,0x0 +0x463,0x0 +0x464,0x0 +0x465,0x0 +0x466,0x0 +0x467,0x0 +0x468,0x0 +0x469,0x0 +0x46a,0x0 +0x46b,0x0 +0x46c,0x0 +0x46d,0x0 +0x46e,0x0 +0x46f,0x0 +0x470,0x0 +0x471,0x0 +0x472,0x0 +0x473,0x0 +0x474,0x0 +0x475,0x0 +0x476,0x0 +0x477,0x0 +0x478,0x0 +0x479,0x0 +0x47a,0x0 +0x47b,0x0 +0x47c,0x0 +0x47d,0x0 +0x47e,0x0 +0x47f,0x0 +0x606,0x0 +0x611,0x0 +0x619,0x0 +0x639,0x0 +0x641,0x0 +0x6e0,0x13b260b8c +0x802,0x0 +0x803,0x50014 +0x808,0x10 +0x80a,0x10 +0x80d,0x1 +0x80f,0x1ff +0x810,0x0 +0x811,0x0 +0x812,0x0 +0x813,0x0 +0x814,0x0 +0x815,0x0 +0x816,0x0 +0x817,0x0 +0x818,0x0 +0x819,0x0 +0x81a,0x0 +0x81b,0x0 +0x81c,0x0 +0x81d,0x0 +0x81e,0x0 +0x81f,0x0 +0x820,0x0 +0x821,0x0 +0x822,0x0 +0x823,0x0 +0x824,0x0 +0x825,0x0 +0x826,0x0 +0x827,0x0 +0x828,0x0 +0x830,0x0 +0x832,0x400ec +0x833,0x10000 +0x834,0x400 +0x835,0x10700 +0x836,0x400 +0x837,0xfe +0x838,0x0 +0x839,0x0 +0x83e,0x0 +0xc0000080,0xd01 +0xc0000081,0x23001000000000 +0xc0000082,0xffffffff81a00080 +0xc0000083,0xffffffff81a015c0 +0xc0000084,0x47700 +0xc0000100,0x7f4e13140740 +0xc0000101,0xffff88803ec00000 +0xc0000102,0x0 +0xc0000103,0x0 +0xc0000104,0x100000000 +0xc0010000,0x0 +0xc0010001,0x0 +0xc0010002,0x0 +0xc0010003,0x0 +0xc0010004,0x0 +0xc0010005,0x0 +0xc0010006,0x0 +0xc0010007,0x0 +0xc0010010,0x0 +0xc0010015,0x0 +0xc001001b,0x20000000 +0xc001001f,0x0 +0xc0010055,0x0 +0xc0010058,0x0 +0xc0010112,0x0 +0xc0010113,0x0 +0xc0010114,0x0 +0xc0010117,0x0 +0xc001011f,0x0 +0xc0010130,0x0 +0xc0010131,0x0 +0xc0010140,0x4 +0xc0010141,0x0 +0xc0010200,0x0 +0xc0010201,0x0 +0xc0010202,0x0 +0xc0010203,0x0 +0xc0010204,0x0 +0xc0010205,0x0 +0xc0010206,0x0 +0xc0010207,0x0 +0xc0010208,0x0 +0xc0010209,0x0 +0xc001020a,0x0 +0xc001020b,0xffff +0xc0011021,0x0 +0xc0011022,0x0 +0xc0011023,0x0 +0xc0011029,0x2 +0xc001102a,0x0 +0xc001102c,0x0 +0x400000000,0x0 +0x2000000000,0x0 +0x4000000000,0x0 +0x8000000000,0x0 +0x1000000000000,0x0 +0x3c000000000000,0x0 +0x80000000000000,0x0 +0x40000000000000,0x0 diff --git a/tests/data/msr/msr_list_T2A_AMD_GENOA_6.1host_6.1guest.csv b/tests/data/msr/msr_list_T2A_AMD_GENOA_6.1host_6.1guest.csv new file mode 100644 index 00000000000..59606121fda --- /dev/null +++ b/tests/data/msr/msr_list_T2A_AMD_GENOA_6.1host_6.1guest.csv @@ -0,0 +1,300 @@ +MSR_ADDR,VALUE +0,0x0 +0x1,0x0 +0x10,0x7bd49b6c +0x11,0x25cb008 +0x12,0x25cc001 +0x17,0x0 +0x1b,0xfee00d00 +0x2a,0x0 +0x2c,0x1000000 +0x34,0x0 +0x3b,0x0 +0x48,0x0 +0x8b,0x1000065 +0xc1,0x0 +0xc2,0x0 +0xcd,0x3 +0xce,0x80000000 +0xfe,0x508 +0x11e,0xbe702111 +0x140,0x0 +0x174,0x10 +0x175,0x3000 +0x176,0x81a01620 +0x179,0x20 +0x17a,0x0 +0x186,0x0 +0x187,0x0 +0x198,0x400000003e8 +0x199,0x0 +0x1a0,0x1 +0x1d9,0x0 +0x1db,0x0 +0x1dc,0x0 +0x1dd,0x0 +0x1de,0x0 +0x1fc,0x0 +0x200,0x0 +0x201,0x0 +0x202,0x0 +0x203,0x0 +0x204,0x0 +0x205,0x0 +0x206,0x0 +0x207,0x0 +0x208,0x0 +0x209,0x0 +0x20a,0x0 +0x20b,0x0 +0x20c,0x0 +0x20d,0x0 +0x20e,0x0 +0x20f,0x0 +0x250,0x0 +0x258,0x0 +0x259,0x0 +0x268,0x0 +0x269,0x0 +0x26a,0x0 +0x26b,0x0 +0x26c,0x0 +0x26d,0x0 +0x26e,0x0 +0x26f,0x0 +0x277,0x7040600070406 +0x2ff,0x0 +0x400,0x0 +0x401,0x0 +0x402,0x0 +0x403,0x0 +0x404,0x0 +0x405,0x0 +0x406,0x0 +0x407,0x0 +0x408,0x0 +0x409,0x0 +0x40a,0x0 +0x40b,0x0 +0x40c,0x0 +0x40d,0x0 +0x40e,0x0 +0x40f,0x0 +0x410,0x0 +0x411,0x0 +0x412,0x0 +0x413,0x0 +0x414,0x0 +0x415,0x0 +0x416,0x0 +0x417,0x0 +0x418,0x0 +0x419,0x0 +0x41a,0x0 +0x41b,0x0 +0x41c,0x0 +0x41d,0x0 +0x41e,0x0 +0x41f,0x0 +0x420,0x0 +0x421,0x0 +0x422,0x0 +0x423,0x0 +0x424,0x0 +0x425,0x0 +0x426,0x0 +0x427,0x0 +0x428,0x0 +0x429,0x0 +0x42a,0x0 +0x42b,0x0 +0x42c,0x0 +0x42d,0x0 +0x42e,0x0 +0x42f,0x0 +0x430,0x0 +0x431,0x0 +0x432,0x0 +0x433,0x0 +0x434,0x0 +0x435,0x0 +0x436,0x0 +0x437,0x0 +0x438,0x0 +0x439,0x0 +0x43a,0x0 +0x43b,0x0 +0x43c,0x0 +0x43d,0x0 +0x43e,0x0 +0x43f,0x0 +0x440,0x0 +0x441,0x0 +0x442,0x0 +0x443,0x0 +0x444,0x0 +0x445,0x0 +0x446,0x0 +0x447,0x0 +0x448,0x0 +0x449,0x0 +0x44a,0x0 +0x44b,0x0 +0x44c,0x0 +0x44d,0x0 +0x44e,0x0 +0x44f,0x0 +0x450,0x0 +0x451,0x0 +0x452,0x0 +0x453,0x0 +0x454,0x0 +0x455,0x0 +0x456,0x0 +0x457,0x0 +0x458,0x0 +0x459,0x0 +0x45a,0x0 +0x45b,0x0 +0x45c,0x0 +0x45d,0x0 +0x45e,0x0 +0x45f,0x0 +0x460,0x0 +0x461,0x0 +0x462,0x0 +0x463,0x0 +0x464,0x0 +0x465,0x0 +0x466,0x0 +0x467,0x0 +0x468,0x0 +0x469,0x0 +0x46a,0x0 +0x46b,0x0 +0x46c,0x0 +0x46d,0x0 +0x46e,0x0 +0x46f,0x0 +0x470,0x0 +0x471,0x0 +0x472,0x0 +0x473,0x0 +0x474,0x0 +0x475,0x0 +0x476,0x0 +0x477,0x0 +0x478,0x0 +0x479,0x0 +0x47a,0x0 +0x47b,0x0 +0x47c,0x0 +0x47d,0x0 +0x47e,0x0 +0x47f,0x0 +0x606,0x0 +0x611,0x0 +0x619,0x0 +0x639,0x0 +0x641,0x0 +0x6e0,0x137e39322 +0x802,0x0 +0x803,0x50014 +0x808,0x10 +0x80a,0x10 +0x80d,0x1 +0x80f,0x1ff +0x810,0x0 +0x811,0x0 +0x812,0x0 +0x813,0x0 +0x814,0x0 +0x815,0x0 +0x816,0x0 +0x817,0x0 +0x818,0x0 +0x819,0x0 +0x81a,0x0 +0x81b,0x0 +0x81c,0x0 +0x81d,0x0 +0x81e,0x0 +0x81f,0x0 +0x820,0x0 +0x821,0x0 +0x822,0x0 +0x823,0x0 +0x824,0x0 +0x825,0x0 +0x826,0x0 +0x827,0x0 +0x828,0x0 +0x830,0x0 +0x832,0x400ec +0x833,0x10000 +0x834,0x400 +0x835,0x10700 +0x836,0x400 +0x837,0xfe +0x838,0x0 +0x839,0x0 +0x83e,0x0 +0xc0000080,0xd01 +0xc0000081,0x23001000000000 +0xc0000082,0xffffffff81a00080 +0xc0000083,0xffffffff81a016e0 +0xc0000084,0x257fd5 +0xc0000100,0x7f7c57cd9740 +0xc0000101,0xffff88803ec00000 +0xc0000102,0x0 +0xc0000103,0x0 +0xc0000104,0x100000000 +0xc0010000,0x0 +0xc0010001,0x0 +0xc0010002,0x0 +0xc0010003,0x0 +0xc0010004,0x0 +0xc0010005,0x0 +0xc0010006,0x0 +0xc0010007,0x0 +0xc0010010,0x0 +0xc0010015,0x0 +0xc001001b,0x20000000 +0xc001001f,0x0 +0xc0010055,0x0 +0xc0010058,0x0 +0xc0010112,0x0 +0xc0010113,0x0 +0xc0010114,0x0 +0xc0010117,0x0 +0xc001011f,0x0 +0xc0010130,0x0 +0xc0010131,0x0 +0xc0010140,0x4 +0xc0010141,0x0 +0xc0010200,0x0 +0xc0010201,0x0 +0xc0010202,0x0 +0xc0010203,0x0 +0xc0010204,0x0 +0xc0010205,0x0 +0xc0010206,0x0 +0xc0010207,0x0 +0xc0010208,0x0 +0xc0010209,0x0 +0xc001020a,0x0 +0xc001020b,0xffff +0xc0011021,0x0 +0xc0011022,0x0 +0xc0011023,0x0 +0xc0011029,0x2 +0xc001102a,0x0 +0xc001102c,0x0 +0x400000000,0x0 +0x2000000000,0x0 +0x4000000000,0x0 +0x8000000000,0x0 +0x1000000000000,0x0 +0x3c000000000000,0x0 +0x80000000000000,0x0 +0x40000000000000,0x0 diff --git a/tests/data/msr/wrmsr_list.txt b/tests/data/msr/wrmsr_list.txt index 87cc6ab1b66..a6a9e387aa4 100644 --- a/tests/data/msr/wrmsr_list.txt +++ b/tests/data/msr/wrmsr_list.txt @@ -1,5 +1,4 @@ 0x1b 0xfee00c00 -0x48 0x1 0x174 0x11 0x17a 0x1 0x1a0 0x0 diff --git a/tests/data/static_cpu_templates/aarch64_with_sve_and_pac.json b/tests/data/static_cpu_templates/aarch64_with_sve_and_pac.json index b155d81dc34..29e47be4a92 100644 --- a/tests/data/static_cpu_templates/aarch64_with_sve_and_pac.json +++ b/tests/data/static_cpu_templates/aarch64_with_sve_and_pac.json @@ -1,4 +1,4 @@ { "kvm_capabilities": ["170", "171", "172"], - "vcpu_features": [{ "index": 0, "bitmap": "0b1110000" }] + "vcpu_features": [{ "index": 0, "bitmap": "0b111xxxx" }] } diff --git a/tests/framework/ab_test.py b/tests/framework/ab_test.py index cf909d44fa6..2ef3e2350a7 100644 --- a/tests/framework/ab_test.py +++ b/tests/framework/ab_test.py @@ -21,7 +21,6 @@ of both invocations is the same, the test passes (with us being alerted to this situtation via a special pipeline that does not block PRs). If not, it fails, preventing PRs from introducing new vulnerable dependencies. """ -import os import statistics from pathlib import Path from tempfile import TemporaryDirectory @@ -31,14 +30,14 @@ from framework import utils from framework.defs import FC_WORKSPACE_DIR -from framework.microvm import Microvm +from framework.properties import global_props from framework.utils import CommandReturn from framework.with_filelock import with_filelock -from host_tools.cargo_build import DEFAULT_TARGET_DIR, get_firecracker_binaries +from host_tools.cargo_build import DEFAULT_TARGET_DIR # Locally, this will always compare against main, even if we try to merge into, say, a feature branch. # We might want to do a more sophisticated way to determine a "parent" branch here. -DEFAULT_A_REVISION = os.environ.get("BUILDKITE_PULL_REQUEST_BASE_BRANCH") or "main" +DEFAULT_A_REVISION = global_props.buildkite_revision_a or "main" T = TypeVar("T") @@ -120,11 +119,6 @@ def binary_ab_test( return result_a, result_b, comparator(result_a, result_b) -def is_pr() -> bool: - """Returns `True` iff we are executing in the context of a build kite run on a pull request""" - return os.environ.get("BUILDKITE_PULL_REQUEST", "false") != "false" - - def git_ab_test_host_command_if_pr( command: str, *, @@ -134,7 +128,7 @@ def git_ab_test_host_command_if_pr( """Runs the given bash command as an A/B-Test if we're in a pull request context (asserting that its stdout and stderr did not change across the PR). Otherwise runs the command, asserting it returns a zero exit code """ - if is_pr(): + if global_props.buildkite_pr: git_ab_test_host_command(command, comparator=comparator) return None @@ -176,56 +170,6 @@ def set_did_not_grow_comparator( ) -def precompiled_ab_test_guest_command( - microvm_factory: Callable[[Path, Path], Microvm], - command: str, - *, - comparator: Callable[[CommandReturn, CommandReturn], bool] = default_comparator, - a_revision: str = DEFAULT_A_REVISION, - b_revision: Optional[str] = None, -): - """The same as git_ab_test_command, but via SSH. The closure argument should setup a microvm using the passed - paths to firecracker and jailer binaries.""" - b_directory = ( - DEFAULT_B_DIRECTORY - if b_revision is None - else FC_WORKSPACE_DIR / "build" / b_revision - ) - - def test_runner(bin_dir, _is_a: bool): - microvm = microvm_factory(bin_dir / "firecracker", bin_dir / "jailer") - return microvm.ssh.run(command) - - (_, old_out, old_err), (_, new_out, new_err), the_same = binary_ab_test( - test_runner, - comparator, - a_directory=FC_WORKSPACE_DIR / "build" / a_revision, - b_directory=b_directory, - ) - - assert ( - the_same - ), f"The output of running command `{command}` changed:\nOld:\nstdout:\n{old_out}\nstderr\n{old_err}\n\nNew:\nstdout:\n{new_out}\nstderr:\n{new_err}" - - -def precompiled_ab_test_guest_command_if_pr( - microvm_factory: Callable[[Path, Path], Microvm], - command: str, - *, - comparator=default_comparator, - check_in_nonpr=True, -): - """The same as git_ab_test_command_if_pr, but via SSH""" - if is_pr(): - precompiled_ab_test_guest_command( - microvm_factory, command, comparator=comparator - ) - return None - - microvm = microvm_factory(*get_firecracker_binaries()) - return microvm.ssh.run(command, check=check_in_nonpr) - - def check_regression( a_samples: List[float], b_samples: List[float], *, n_resamples: int = 9999 ): diff --git a/tests/framework/artifacts.py b/tests/framework/artifacts.py index f4e05a88c94..0ed27c16b61 100644 --- a/tests/framework/artifacts.py +++ b/tests/framework/artifacts.py @@ -32,35 +32,31 @@ def select_supported_kernels(): return supported_kernels -def kernels(glob) -> Iterator: +def kernels(glob, artifact_dir: Path = ARTIFACT_DIR) -> Iterator: """Return supported kernels as kernels supported by the current combination of kernel and instance type. """ supported_kernels = select_supported_kernels() - for kernel in sorted(ARTIFACT_DIR.rglob(glob)): + for kernel in sorted(artifact_dir.glob(glob)): for kernel_regex in supported_kernels: if re.fullmatch(kernel_regex, kernel.name): yield kernel break -def disks(glob) -> Iterator: +def disks(glob) -> list: """Return supported rootfs""" - yield from sorted(ARTIFACT_DIR.glob(glob)) + return sorted(ARTIFACT_DIR.glob(glob)) -def kernel_params(glob="vmlinux-*", select=kernels) -> Iterator: +def kernel_params( + glob="vmlinux-*", select=kernels, artifact_dir=ARTIFACT_DIR +) -> Iterator: """Return supported kernels""" - for kernel in select(glob): + for kernel in select(glob, artifact_dir): yield pytest.param(kernel, id=kernel.name) -def rootfs_params(glob="ubuntu-*.squashfs") -> Iterator: - """Return supported rootfs as pytest parameters""" - for rootfs in disks(glob=glob): - yield pytest.param(rootfs, id=rootfs.name) - - @dataclass(frozen=True, repr=True) class FirecrackerArtifact: """Utility class for Firecracker binary artifacts.""" diff --git a/tests/framework/defs.py b/tests/framework/defs.py index f017dc231ee..aec3568a85d 100644 --- a/tests/framework/defs.py +++ b/tests/framework/defs.py @@ -26,9 +26,12 @@ # Absolute path to the test results folder TEST_RESULTS_DIR = FC_WORKSPACE_DIR / "test_results" -# The minimum required host kernel version for which io_uring is supported in -# Firecracker. -MIN_KERNEL_VERSION_FOR_IO_URING = "5.10.51" +DEFAULT_BINARY_DIR = ( + LOCAL_BUILD_PATH + / "cargo_target" + / f"{platform.machine()}-unknown-linux-musl" + / "release" +) SUPPORTED_HOST_KERNELS = ["5.10", "6.1"] diff --git a/tests/framework/gitlint_rules.py b/tests/framework/gitlint_rules.py index fad6ccb47d2..7f38cf11eab 100644 --- a/tests/framework/gitlint_rules.py +++ b/tests/framework/gitlint_rules.py @@ -2,6 +2,8 @@ # SPDX-License-Identifier: Apache-2.0 """The user defined rules for gitlint.""" +import re + from gitlint.rules import CommitRule, RuleViolation @@ -22,16 +24,20 @@ class EndsSigned(CommitRule): def validate(self, commit): r"""Validates Signed-off-by and Co-authored-by tags as Linux's scripts/checkpatch.pl - >>> from gitlint.tests.base import BaseTestCase + >>> from gitlint.git import GitContext >>> from gitlint.rules import RuleViolation ... >>> ends_signed = EndsSigned() + >>> miss_sob_follows_coab = "Missing 'Signed-off-by' following 'Co-authored-by'" + >>> miss_sob = "'Signed-off-by' not found in commit message body" + >>> non_sign = "Non 'Co-authored-by' or 'Signed-off-by' string found following 1st 'Signed-off-by'" + >>> email_no_match = "'Co-authored-by' and 'Signed-off-by' name/email do not match" ... >>> msg1 = ( ... f"Title\n\nMessage.\n\n" ... f"Signed-off-by: name " ... ) - >>> commit1 = BaseTestCase.gitcommit(msg1) + >>> commit1 = GitContext.from_commit_msg(msg1).commits[0] >>> ends_signed.validate(commit1) [] >>> msg2 = ( @@ -39,52 +45,41 @@ def validate(self, commit): ... f"Co-authored-by: name \n\n" ... f"Signed-off-by: name " ... ) - >>> commit2 = BaseTestCase.gitcommit(msg2) + >>> commit2 = GitContext.from_commit_msg(msg2).commits[0] >>> ends_signed.validate(commit2) [] - >>> msg3 = ( - ... f"Title\n\nMessage.\n\n" - ... ) - >>> commit3 = BaseTestCase.gitcommit(msg3) + >>> msg3 = f"Title\n\nMessage.\n\n" + >>> commit3 = GitContext.from_commit_msg(msg3).commits[0] >>> vio3 = ends_signed.validate(commit3) - >>> vio_msg3 = ( - ... f"'Signed-off-by:' not found in commit message body" - ... ) - >>> vio3 == [RuleViolation("UC2", vio_msg3)] + >>> vio3 == [RuleViolation("UC2", miss_sob)] True >>> msg4 = ( ... f"Title\n\nMessage.\n\n" ... f"Signed-off-by: name \n\na sentence" ... ) - >>> commit4 = BaseTestCase.gitcommit(msg4) + >>> commit4 = GitContext.from_commit_msg(msg4).commits[0] >>> vio4 = ends_signed.validate(commit4) - >>> vio_msg4 = ( - ... f"Non 'Co-authored-by:' or 'Signed-off-by:' string found following 1st 'Signed-off-by:'" - ... ) - >>> vio4 == [RuleViolation("UC2", vio_msg4, None, 5)] + >>> vio4 == [RuleViolation("UC2", non_sign, None, 6)] True >>> msg5 = ( ... f"Title\n\nMessage.\n\n" ... f"Co-authored-by: name " ... ) - >>> commit5 = BaseTestCase.gitcommit(msg5) + >>> commit5 = GitContext.from_commit_msg(msg5).commits[0] >>> vio5 = ends_signed.validate(commit5) - >>> vio_msg5 = ( - ... f"Missing 'Signed-off-by:' following 'Co-authored-by:'" - ... ) - >>> vio5 == [RuleViolation("UC2", vio_msg5, None, 2)] + >>> vio5 == [ + ... RuleViolation("UC2", miss_sob, None, None), + ... RuleViolation("UC2", miss_sob_follows_coab, None, 5) + ... ] True >>> msg6 = ( ... f"Title\n\nMessage.\n\n" ... f"Co-authored-by: name \n\n" ... f"Signed-off-by: different name " ... ) - >>> commit6 = BaseTestCase.gitcommit(msg6) + >>> commit6 = GitContext.from_commit_msg(msg6).commits[0] >>> vio6 = ends_signed.validate(commit6) - >>> vio_msg6 = ( - ... f"'Co-authored-by:' and 'Signed-off-by:' name/email do not match" - ... ) - >>> vio6 == [RuleViolation("UC2", vio_msg6, None, 6)] + >>> vio6 == [RuleViolation("UC2", email_no_match, None, 6)] True """ @@ -92,59 +87,50 @@ def validate(self, commit): # Utilities def vln(stmt, i): - return RuleViolation(self.id, stmt, None, i) - - co_auth = "Co-authored-by:" - sig = "Signed-off-by:" + violations.append(RuleViolation(self.id, stmt, None, i)) - message_iter = enumerate(commit.message.original.split("\n")) + coab = "Co-authored-by" + sob = "Signed-off-by" - # Skip ahead to the first signoff or co-author tag - - # Checks commit message contains a `Signed-off-by` string - for i, line in message_iter: - if line.startswith(sig) or line.startswith(co_auth): - break - else: - # No signature was found in the message (before `message_iter` ended) - # This check here can have false-negatives (e.g. if the body ends with only - # a 'Co-authored-by' tag), but then below will realize that the co-authored-by - # tag isnt followed by a Signed-off-by tag and fail (and also the DCO check will - # complain). - violations.append(vln(f"'{sig}' not found in commit message body", None)) - - # Check that from here on out we only have signatures and co-authors, and that - # every co-author is immediately followed by a signature with the same name/email. - for i, line in message_iter: - if line.startswith(co_auth): - try: - _, next_line = next(message_iter) - except StopIteration: - violations.append( - vln(f"Missing '{sig}' tag following '{co_auth}'", i) - ) - else: - if not next_line.startswith(sig): - violations.append( - vln(f"Missing '{sig}' tag following '{co_auth}'", i + 1) - ) - continue - - if next_line.split(":")[1].strip() != line.split(":")[1].strip(): - violations.append( - vln(f"{co_auth} and {sig} name/email do not match", i + 1) - ) - continue - - if line.startswith(sig) or not line.strip(): + # find trailers + trailers = [] + for i, line in enumerate(commit.message.original.splitlines()): + # ignore empty lines + if not line: continue + match = re.match(r"([\w-]+):\s+(.*)", line) + if match: + key, val = match.groups() + trailers.append((i, key, val)) + else: + trailers.append((i, "line", line)) + # artificial line so we can check any "previous line" rules + trailers.append((trailers[-1][0] + 1, None, None)) - violations.append( + # Checks commit message contains a `Signed-off-by` string + if not [x for x in trailers if x[1] == sob]: + vln(f"'{sob}' not found in commit message body", None) + + prev_trailer, prev_value = None, None + sig_trailers = False + for i, trailer, value in trailers: + if trailer in {sob, coab}: + sig_trailers = True + elif trailer not in {sob, coab, None} and sig_trailers: vln( - f"Non '{co_auth}' or '{sig}' string found following 1st '{sig}'", + f"Non '{coab}' or '{sob}' string found following 1st '{sob}'", i, ) - ) + # Every co-author is immediately followed by a signature + if prev_trailer == coab: + if trailer != sob: + vln(f"Missing '{sob}' following '{coab}'", i) + else: + # with the same name/email. + if value != prev_value: + vln(f"'{coab}' and '{sob}' name/email do not match", i) + + prev_trailer, prev_value = trailer, value # Return errors return violations diff --git a/tests/framework/http_api.py b/tests/framework/http_api.py index a1ee37174b0..ea8efd3df4f 100644 --- a/tests/framework/http_api.py +++ b/tests/framework/http_api.py @@ -3,8 +3,6 @@ """A simple HTTP client for the Firecracker API""" -# pylint:disable=too-few-public-methods - import urllib from http import HTTPStatus diff --git a/tests/framework/microvm.py b/tests/framework/microvm.py index ed02488bc7b..9ee3c85b0bb 100644 --- a/tests/framework/microvm.py +++ b/tests/framework/microvm.py @@ -38,6 +38,7 @@ from framework.microvm_helpers import MicrovmHelpers from framework.properties import global_props from framework.utils_drive import VhostUserBlkBackend, VhostUserBlkBackendType +from framework.utils_uffd import spawn_pf_handler, uffd_handler from host_tools.fcmetrics import FCMetricsMonitor from host_tools.memory import MemoryMonitor @@ -75,6 +76,7 @@ class Snapshot: disks: dict ssh_key: Path snapshot_type: SnapshotType + meta: dict @property def is_diff(self) -> bool: @@ -110,6 +112,7 @@ def copy_to_chroot(self, chroot) -> "Snapshot": disks=self.disks, ssh_key=self.ssh_key, snapshot_type=self.snapshot_type, + meta=self.meta, ) @classmethod @@ -125,6 +128,7 @@ def load_from(cls, src: Path) -> "Snapshot": disks={dsk: src / p for dsk, p in obj["disks"].items()}, ssh_key=src / obj["ssh_key"], snapshot_type=SnapshotType(obj["snapshot_type"]), + meta=obj["meta"], ) def save_to(self, dst: Path): @@ -147,6 +151,7 @@ def save_to(self, dst: Path): "disks": new_disks, "ssh_key": self.ssh_key.name, "snapshot_type": self.snapshot_type.value, + "meta": self.meta, } snap_json = dst / "snapshot.json" snap_json.write_text(json.dumps(obj)) @@ -184,6 +189,7 @@ def __init__( monitor_memory: bool = True, jailer_kwargs: Optional[dict] = None, numa_node=None, + custom_cpu_template: Path = None, ): """Set up microVM attributes, paths, and data structures.""" # pylint: disable=too-many-statements @@ -196,6 +202,7 @@ def __init__( self.ssh_key = None self.initrd_file = None self.boot_args = None + self.uffd_handler = None self.fc_binary_path = Path(fc_binary_path) assert fc_binary_path.exists() @@ -241,6 +248,12 @@ def __init__( self.disks_vhost_user = {} self.vcpus_count = None self.mem_size_bytes = None + self.cpu_template_name = None + # The given custom CPU template will be set in basic_config() but could + # be overwritten via set_cpu_template(). + self.custom_cpu_template = custom_cpu_template + + self._connections = [] self._pre_cmd = [] if numa_node: @@ -277,6 +290,10 @@ def kill(self): for monitor in self.monitors: monitor.stop() + # Kill all background SSH connections + for connection in self._connections: + connection.close() + # We start with vhost-user backends, # because if we stop Firecracker first, the backend will want # to exit as well and this will cause a race condition. @@ -295,6 +312,9 @@ def kill(self): if self.screen_pid: os.kill(self.screen_pid, signal.SIGKILL) except: + LOG.error( + "Failed to kill Firecracker Process. Did it already die (or did the UFFD handler process die and take it down)?" + ) LOG.error(self.log_data) raise @@ -370,7 +390,7 @@ def _validate_api_response_times(self): "Got API call duration log entry before request entry" ) - if current_call.url != "/snapshot/create": + if current_call.url not in ["/snapshot/create", "/snapshot/load"]: exec_time = float(match.group("execution_time")) / 1000.0 assert ( @@ -732,12 +752,17 @@ def basic_config( smt=smt, mem_size_mib=mem_size_mib, track_dirty_pages=track_dirty_pages, - cpu_template=cpu_template, huge_pages=huge_pages, ) self.vcpus_count = vcpu_count self.mem_size_bytes = mem_size_mib * 2**20 + if self.custom_cpu_template is not None: + self.set_cpu_template(self.custom_cpu_template) + + if cpu_template is not None: + self.set_cpu_template(cpu_template) + if self.memory_monitor: self.memory_monitor.start() @@ -770,6 +795,19 @@ def basic_config( if enable_entropy_device: self.enable_entropy_device() + def set_cpu_template(self, cpu_template): + """Set guest CPU template.""" + if cpu_template is None: + return + # static CPU template + if isinstance(cpu_template, str): + self.api.machine_config.patch(cpu_template=cpu_template) + self.cpu_template_name = cpu_template.lower() + # custom CPU template + elif isinstance(cpu_template, dict): + self.api.cpu_config.put(**cpu_template["template"]) + self.cpu_template_name = cpu_template["name"].lower() + def add_drive( self, drive_id, @@ -917,6 +955,10 @@ def make_snapshot( net_ifaces=[x["iface"] for ifname, x in self.iface.items()], ssh_key=self.ssh_key, snapshot_type=snapshot_type, + meta={ + "kernel_file": str(self.kernel_file), + "vcpus_count": self.vcpus_count, + }, ) def snapshot_diff(self, *, mem_path: str = "mem", vmstate_path="vmstate"): @@ -929,39 +971,71 @@ def snapshot_full(self, *, mem_path: str = "mem", vmstate_path="vmstate"): def restore_from_snapshot( self, - snapshot: Snapshot, + snapshot: Snapshot = None, resume: bool = False, - uffd_path: Path = None, + rename_interfaces: dict = None, ): """Restore a snapshot""" - jailed_snapshot = snapshot.copy_to_chroot(Path(self.chroot())) + if self.uffd_handler is None: + assert ( + snapshot is not None + ), "snapshot file must be provided if no uffd handler is attached!" + + jailed_snapshot = snapshot.copy_to_chroot(Path(self.chroot())) + else: + jailed_snapshot = self.uffd_handler.snapshot + jailed_mem = Path("/") / jailed_snapshot.mem.name jailed_vmstate = Path("/") / jailed_snapshot.vmstate.name - snapshot_disks = [v for k, v in snapshot.disks.items()] + snapshot_disks = [v for k, v in jailed_snapshot.disks.items()] assert len(snapshot_disks) > 0, "Snapshot requires at least one disk." jailed_disks = [] for disk in snapshot_disks: jailed_disks.append(self.create_jailed_resource(disk)) - self.disks = snapshot.disks - self.ssh_key = snapshot.ssh_key + self.disks = jailed_snapshot.disks + self.ssh_key = jailed_snapshot.ssh_key # Create network interfaces. - for iface in snapshot.net_ifaces: + for iface in jailed_snapshot.net_ifaces: self.add_net_iface(iface, api=False) mem_backend = {"backend_type": "File", "backend_path": str(jailed_mem)} - if uffd_path is not None: - mem_backend = {"backend_type": "Uffd", "backend_path": str(uffd_path)} + if self.uffd_handler is not None: + mem_backend = { + "backend_type": "Uffd", + "backend_path": str(self.uffd_handler.socket_path), + } + + for key, value in jailed_snapshot.meta.items(): + setattr(self, key, value) + # Adjust things just in case + self.kernel_file = Path(self.kernel_file) + + iface_overrides = [] + if rename_interfaces is not None: + iface_overrides = [ + {"iface_id": k, "host_dev_name": v} + for k, v in rename_interfaces.items() + ] + + optional_kwargs = {} + if iface_overrides: + # For backwards compatibility ab testing we want to avoid adding + # new parameters until we have a release baseline with the new + # parameter. Once the release baseline has moved, this assignment + # can be inline in the snapshot_load command below + optional_kwargs["network_overrides"] = iface_overrides self.api.snapshot_load.put( mem_backend=mem_backend, snapshot_path=str(jailed_vmstate), - enable_diff_snapshots=snapshot.is_diff, + enable_diff_snapshots=jailed_snapshot.is_diff, resume_vm=resume, + **optional_kwargs, ) # This is not a "wait for boot", but rather a "VM still works after restoration" - if snapshot.net_ifaces and resume: + if jailed_snapshot.net_ifaces and resume: self.wait_for_ssh_up() return jailed_snapshot @@ -978,13 +1052,16 @@ def ssh_iface(self, iface_idx=0): """Return a cached SSH connection on a given interface id.""" guest_ip = list(self.iface.values())[iface_idx]["iface"].guest_ip self.ssh_key = Path(self.ssh_key) - return net_tools.SSHConnection( + connection = net_tools.SSHConnection( netns=self.netns.id, ssh_key=self.ssh_key, user="root", host=guest_ip, + control_path=Path(self.chroot()) / f"ssh-{iface_idx}.sock", on_error=self._dump_debug_information, ) + self._connections.append(connection) + return connection @property def ssh(self): @@ -1025,12 +1102,25 @@ def wait_for_ssh_up(self): class MicroVMFactory: """MicroVM factory""" - def __init__(self, fc_binary_path: Path, jailer_binary_path: Path, **kwargs): + def __init__(self, binary_path: Path, **kwargs): self.vms = [] - self.fc_binary_path = Path(fc_binary_path) - self.jailer_binary_path = Path(jailer_binary_path) + self.binary_path = binary_path + self.netns_factory = kwargs.pop("netns_factory", net_tools.NetNs) self.kwargs = kwargs + assert self.fc_binary_path.exists(), "missing firecracker binary" + assert self.jailer_binary_path.exists(), "missing jailer binary" + + @property + def fc_binary_path(self): + """The path to the firecracker binary from which this factory will build VMs""" + return self.binary_path / "firecracker" + + @property + def jailer_binary_path(self): + """The path to the jailer binary using which this factory will build VMs""" + return self.binary_path / "jailer" + def build(self, kernel=None, rootfs=None, **kwargs): """Build a microvm""" kwargs = self.kwargs | kwargs @@ -1041,7 +1131,7 @@ def build(self, kernel=None, rootfs=None, **kwargs): jailer_binary_path=kwargs.pop( "jailer_binary_path", self.jailer_binary_path ), - netns=kwargs.pop("netns", net_tools.NetNs(microvm_id)), + netns=kwargs.pop("netns", self.netns_factory(microvm_id)), **kwargs, ) vm.netns.setup() @@ -1059,6 +1149,64 @@ def build(self, kernel=None, rootfs=None, **kwargs): vm.ssh_key = ssh_key return vm + def build_from_snapshot(self, snapshot: Snapshot): + """Build a microvm from a snapshot""" + vm = self.build() + vm.spawn() + vm.restore_from_snapshot(snapshot, resume=True) + return vm + + def build_n_from_snapshot( + self, + current_snapshot, + nr_vms, + *, + uffd_handler_name=None, + incremental=False, + use_snapshot_editor=True, + ): + """A generator of `n` microvms restored, either all restored from the same given snapshot + (incremental=False), or created by taking successive snapshots of restored VMs + """ + last_snapshot = None + for _ in range(nr_vms): + microvm = self.build() + microvm.spawn() + + if uffd_handler_name is not None: + spawn_pf_handler( + microvm, + uffd_handler(uffd_handler_name, binary_dir=self.binary_path), + current_snapshot, + ) + + snapshot_copy = microvm.restore_from_snapshot(current_snapshot, resume=True) + + yield microvm + + if incremental: + # When doing diff snapshots, we continuously overwrite the same base snapshot file from the first + # iteration in-place with successive snapshots, so don't delete it! + if last_snapshot is not None and not last_snapshot.is_diff: + last_snapshot.delete() + + next_snapshot = microvm.make_snapshot(current_snapshot.snapshot_type) + + if current_snapshot.is_diff: + next_snapshot = next_snapshot.rebase_snapshot( + current_snapshot, use_snapshot_editor + ) + + last_snapshot = current_snapshot + current_snapshot = next_snapshot + + microvm.kill() + snapshot_copy.delete() + + if last_snapshot is not None and not last_snapshot.is_diff: + last_snapshot.delete() + current_snapshot.delete() + def kill(self): """Clean up all built VMs""" for vm in self.vms: diff --git a/tests/framework/microvm_helpers.py b/tests/framework/microvm_helpers.py index 4239eb2489f..b34da3c447e 100644 --- a/tests/framework/microvm_helpers.py +++ b/tests/framework/microvm_helpers.py @@ -222,3 +222,26 @@ def run_in_netns(cmd): # add a route on the host for the clone address run(f"ip route add {ingress_ipv4} via {veth_guest_ip}") + + def trace_cmd_guest(self, fns, cmd, port=4321): + """Run trace-cmd on the guest, but transfer the data directly to the host.""" + docker_apt_install("trace-cmd") + print("host> trace-cmd listen") + _proc = subprocess.Popen( + [ + "ip", + "netns", + "exec", + self.vm.netns.id, + "trace-cmd", + "listen", + "-p", + str(port), + ] + ) + print("guest> trace-cmd record") + host_ip = self.vm.iface["eth0"]["iface"].host_ip + _guest_ps = self.vm.ssh.run( + f"trace-cmd record -N {host_ip}:{port} -p function {' '.join(fns)} {cmd}" + ) + return list(Path(".").glob("trace.*.dat")) diff --git a/tests/framework/properties.py b/tests/framework/properties.py index b40df56249e..c7c9dfe789d 100644 --- a/tests/framework/properties.py +++ b/tests/framework/properties.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 # pylint:disable=broad-except -# pylint:disable=too-few-public-methods """ Metadata we want to attach to tests for further analysis and troubleshooting @@ -27,7 +26,7 @@ def get_os_version(): """Get the OS version >>> get_os_version() - Ubuntu 18.04.6 LTS + 'Ubuntu 24.04.1 LTS' """ os_release = Path("/etc/os-release").read_text(encoding="ascii") @@ -42,7 +41,7 @@ def get_host_os(kv: str = None): This only works for AL2 and AL2023 >>> get_host_os("6.1.41-63.118.amzn2023.x86_64") - amzn2023 + 'amzn2023' """ if kv is None: kv = platform.release() @@ -72,11 +71,14 @@ def __init__(self): # major.minor.patch self.host_linux_patch = get_kernel_version(2) self.os = get_os_version() - self.host_os = get_host_os() + self.host_os = get_host_os() or "NA" self.libc_ver = "-".join(platform.libc_ver()) self.rust_version = run_cmd("rustc --version |awk '{print $2}'") + # Buildkite/PR information self.buildkite_pipeline_slug = os.environ.get("BUILDKITE_PIPELINE_SLUG") self.buildkite_build_number = os.environ.get("BUILDKITE_BUILD_NUMBER") + self.buildkite_pr = os.environ.get("BUILDKITE_PULL_REQUEST", "false") != "false" + self.buildkite_revision_a = os.environ.get("BUILDKITE_PULL_REQUEST_BASE_BRANCH") if self._in_git_repo(): self.git_commit_id = run_cmd("git rev-parse HEAD") diff --git a/tests/framework/state_machine.py b/tests/framework/state_machine.py index 97975c75526..1d8dd664e6b 100644 --- a/tests/framework/state_machine.py +++ b/tests/framework/state_machine.py @@ -3,8 +3,6 @@ """Defines a stream based string matcher and a generic state object.""" -# Too few public methods (1/2) (too-few-public-methods) -# pylint: disable=R0903 class MatchStaticString: """Match a static string versus input.""" diff --git a/tests/framework/static_analysis.py b/tests/framework/static_analysis.py new file mode 100644 index 00000000000..f75f938a27a --- /dev/null +++ b/tests/framework/static_analysis.py @@ -0,0 +1,629 @@ +# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Module to perform a static analysis of a binary to determine all +actually invoked syscalls. Compares these against seccomp filters, and lists +redundant rules (e.g. those never triggered because the syscall they allow is not +actually used in the binary).""" + +import functools +import json +import logging +import platform +import re +import subprocess +from abc import ABC, abstractmethod +from collections import defaultdict +from dataclasses import dataclass +from pathlib import Path +from typing import Callable, ClassVar, Generic, Tuple, TypeVar, get_args + +import seccomp + +logger = logging.getLogger(__name__) + +# pylint: disable=c-extension-no-member,too-many-return-statements,too-few-public-methods + + +@dataclass +class Instruction(ABC): + """ABC representing a single assembly instruction""" + + mnemonic: str + args: list[str] + + comment_prefix: ClassVar[str] + + @property + @abstractmethod + def is_call(self): + """Checks whether the given instruction is a subroutine call""" + + @property + @abstractmethod + def is_syscall(self): + """Checks whether the given instruction is a syscall instruction""" + + @classmethod + def from_str(cls, insn_str): + """Parses the given string as a single assembly instruction, in the syntax that + objdump uses by default on this architecture""" + # remove comments + insn_str = re.sub(rf"\s+{cls.comment_prefix}.*", "", insn_str) + parts = insn_str.split(maxsplit=1) + args = [] + if len(parts) > 1: + # Strip each argument, in case objdump decides to put , + # spaces after commas (happens on ARM, doesn't happen on x86) + args = [x.strip() for x in parts[1].split(",")] + return cls(parts[0], args) + + @abstractmethod + def backpropagate_register( + self, reg: str + ) -> str | int | Tuple[str, Callable[[int], int]]: + """ + If this instruction loads an immediate into the given register, returns + that immediate as an integer. If the instruction is a register to register transfer, + returns the source register for this transfer. If this instruction doesn't change + the given register, returns the given register. Returns None if we don't know + how to backpropagate through this instruction. + + :param reg: the register to backpropagate through this instruction + :return: An integer if the register is loaded with an immediate by this instruction, or a register + which needs to be backpropagated further (together with an optional forward-propagation + function). + """ + + def __str__(self): + return f"{self.mnemonic} {','.join(self.args)}" + + +class InstructionX86_64(Instruction): # pylint: disable=invalid-name + """A x86_64 instruction""" + + comment_prefix = "#" + + @property + def is_call(self): + return self.mnemonic in ["call", "jmp"] + + @property + def is_syscall(self): + return self.mnemonic == "syscall" + + def backpropagate_register( + self, reg: str + ) -> str | int | Tuple[str, Callable[[int], int]]: + # Simplifying assumption: an instruction will not modify a register + # that it doesn't reference (generally wrong, but fine for our purposes) + affected_registers = [ + match for (match, _) in re.findall(r"(%[a-z0-9]{2,4})(\W|)", str(self)) + ] + if reg not in affected_registers: + return reg + + match self.mnemonic: + case "mov": + if len(self.args) != 2: + raise UnsupportedInstructionError(self, reg) + + src, dst = self.args + + if dst == reg: + # an immediate load + if src.startswith("$"): + return int(src[3:], 16) + # We moved something into our target register. If it's a new register, we understand + # what's going on. Anything else, and tough luck + if re.match(r"^%\w{2,4}$", src): + return src + raise UnsupportedInstructionError(self, reg) + return reg + case "xor": + src, dst = self.args + + if src == dst: + # we know that reg is part of the arguments, and we know that the arguments are identical + # Thus we have xor reg,reg, which is effectively zeroing reg + return 0 + case "push": + # a push doesn't do anything + return reg + + raise UnsupportedInstructionError(self, reg) + + +class InstructionAarch64(Instruction): + """An aarch64 assembly instruction""" + + comment_prefix = "//" + + @property + def is_call(self): + return self.mnemonic in ["b", "bl"] + + @property + def is_syscall(self): + return self.mnemonic == "svc" and self.args == ["#0x0"] + + def backpropagate_register( + self, reg: str + ) -> str | int | Tuple[str, Callable[[int], int]]: + affected_registers = [ + match + for (_, match, _) in re.findall(r"(\s|,)([wx]\d{1,2})(\W|)", str(self)) + ] + if reg not in affected_registers: + return reg + + match self.mnemonic: + case "mov": + if len(self.args) != 2: + raise UnsupportedInstructionError(self, reg) + + dst, src = self.args + + if dst == reg: + # an immediate load + if src.startswith("#"): + return int(src[3:], 16) + + if src in ["xzr", "wzr"]: + # See https://developer.arm.com/documentation/102374/0102/Registers-in-AArch64---other-registers + return 0 + + # We moved something into our target register. If it's a new register, we understand + # what's going on. Anything else, and tough luck + if re.match(r"^[xw]\d{1,2}$", src): + return src + + raise UnsupportedInstructionError(self, reg) + return reg + case "movk": + # https://developer.arm.com/documentation/dui0802/a/A64-General-Instructions/MOVK + assert len(self.args) in [2, 3], str(self) + + immediate = int(self.args[1][3:], 16) + shift = 0 + if len(self.args) == 3: + # shift has form "lsl #", so strip first 5 characters + shift = int(self.args[2][5:]) + + mask = 0b1111_1111_1111_1111 << shift + + return reg, lambda x: (x & ~mask) | (immediate << shift) + case "add" | "sub": + if len(self.args) != 3: + raise UnsupportedInstructionError(self, reg) + + dst, src, imm = self.args + + if dst != reg: + return reg + + try: + # We can only handle additions of constants, because + # the backpropagation algorithm cannot follow multiple registers. + imm = int(imm[3:], 16) + except ValueError as exc: + raise UnsupportedInstructionError(self, reg) from exc + + if self.mnemonic == "add": + return src, lambda x: x + imm + # must have self.mnemonic == "sub" here by the case label above. + return src, lambda x: x - imm + + raise UnsupportedInstructionError(self, reg) + + +TInstruction = TypeVar( # pylint: disable=invalid-name + "TInstruction", bound=Instruction +) + + +class Architecture(Generic[TInstruction]): + """ABC representing an instruction set architecture, specifically containing information + pertaining to syscall and subroutine call conventions""" + + # The symbolic name of the register used to pass the syscall number to the architectures + # syscall instruction + syscall_nr_register: ClassVar[str] + + # The list of registers (in order) used to pass arguments to the architectures syscall instruction + syscall_argument_registers: ClassVar[list[str]] + + # The list of registers (in order) used to pass arguments to normal function calls + fn_call_argument_registers: ClassVar[list[str]] + + # Convert to the correct variant of seccomp's Arch enum + seccomp_arch: ClassVar[seccomp.Arch] + + t_instruction: type + + def __init_subclass__(cls) -> None: + # Determine the generic parameter of a subclass, and store it in t_instruction. pylint doesnt understand it + # pylint: disable=no-member + cls.t_instruction = get_args(cls.__orig_bases__[0])[0] + + @staticmethod + @abstractmethod + def generalize_reg(reg: str) -> list[str]: + """For a given register, return a list of registers that partially alias it. + + E.g. on x86, when given %rdi as input, return [%rdi, %edi, %di]""" + + @classmethod + def determine_register_value(cls, instructions: list[TInstruction], register: str): + """Determines the value of the given register at the end of the given instruction sequence + via backpropagation""" + looking_for = cls.generalize_reg(register) + transforms = [] + + for insn in reversed(instructions): + for reg in looking_for: + next_reg = insn.backpropagate_register(reg) + + if isinstance(next_reg, tuple): + next_reg, transform = next_reg + + transforms.insert(0, transform) + + if isinstance(next_reg, int): + # Apply all transforms in reverse order of which we discovered them: We now forward propagate + # the actual value! + return functools.reduce( + lambda acc, fn: fn(acc), transforms, next_reg + ) + + if next_reg != reg: + looking_for = cls.generalize_reg(next_reg) + break + + raise BackpropagationReachedStartOfFn(looking_for) + + +class ArchitectureX86_64( # pylint: disable=invalid-name + Architecture[InstructionX86_64] +): + """The x86_64 ISA""" + + syscall_nr_register = "%eax" + syscall_argument_registers = ["%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9"] + fn_call_argument_registers = ["%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"] + seccomp_arch = seccomp.Arch.X86_64 + + @staticmethod + def generalize_reg(reg: str) -> list[str]: + suffixes = ["ax", "bx", "cx", "dx", "si", "di", "bp", "sp"] + prefixes = ["%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"] + + for suffix in suffixes: + if reg.endswith(suffix): + return [f"%r{suffix}", f"%e{suffix}", f"%{suffix}"] + + for prefix in prefixes: + if reg.startswith(prefix): + return [prefix, f"{prefix}d", f"{prefix}w"] + + return [reg] + + +class ArchitectureAarch64(Architecture[InstructionAarch64]): + """The aarch64 ISA""" + + ALL_REGS = [f"x{i}" for i in range(0, 32)] + + syscall_nr_register = "x8" + syscall_argument_registers = ALL_REGS[:8] + fn_call_argument_registers = ALL_REGS[:8] + seccomp_arch = seccomp.Arch.AARCH64 + + @staticmethod + def generalize_reg(reg: str) -> list[str]: + mtch = re.match(r"^[xw](\d{1,2})$", reg) + + if mtch: + nr = mtch.group(1) + + return [f"x{nr}", f"w{nr}"] + + return [reg] + + +SYSCALL_WRAPPERS = ["syscall", "__syscall_cp", "__syscall_cp_c"] +SPECIFIC_SYSCALL_WRAPPERS = { + "ioctl": {"syscall": "ioctl", "nargs": 3}, + "__mmap": {"syscall": "mmap", "nargs": 6}, + "socket": {"syscall": "socket", "nargs": 3}, + "__madvise": {"syscall": "madvise", "nargs": 3}, + # special snowflake ioctl: https://github.com/kraj/musl/blob/ffb23aef7b5339b8c3234f4c6a93c488dc873919/src/termios/tcsetattr.c#L5 + "tcsetattr": { + "syscall": "ioctl", + "nargs": 3, + "arg_transform": {1: lambda x: x + 0x5402}, + }, +} + + +class Function: + """Represents a single function in the binary (e.g. as determined from DWARF debug information)""" + + def __init__(self, name: str, arch: Architecture): + self.name = name + self.instructions = [] + self.arch = arch + + def resolve_registers_before_insn(self, i: int, registers: list[str]): + """Tries to determine the values of the given registers when the i-th instruction + executes.""" + resolved_registers = {} + + for reg in registers: + try: + resolved_registers[reg] = self.arch.determine_register_value( + self.instructions[:i], reg + ) + except ( + UnsupportedInstructionError, + BackpropagationReachedStartOfFn, + ) as exc: + resolved_registers[reg] = exc + + return resolved_registers + + +class UnsupportedInstructionError(Exception): + """Exception indicating that an unsupported instruction was encountered during backpropagation, and this + unsupported instruction refers to the register being backpropagated.""" + + def __init__(self, insn: Instruction, reg: str): + super().__init__( + f"Encountered unsupported instruction during backpropagation which affects a register of interest ({reg}): {insn}" + ) + + self.instruction = insn + + +class BackpropagationReachedStartOfFn(Exception): + """Exception indicating that the beginning of a function was reached during backpropagation, without any immediate + value being loaded into the register whose value we were trying to determine""" + + def __init__(self, current_register): + super().__init__( + f"Backpropagation reached beginning of function definition while backpropagating {current_register}. Maybe it is a parameter itself?" + ) + + +def parse_objdump_output(output: str, arch: Architecture) -> list[Function]: + """Parse the stdout from obj dump into a list of the contained functions""" + lines = output.splitlines() + + # Skip the first line of the output, it's just the file format + lines = lines[2:] + + functions = [] + current_function = None + + for line in lines: + line = line.strip() + + # Skip empty lines and those just announcing the start of a new section + if not line or line.startswith("Disassembly of section"): + # all functions are separated by empty lines. This is a sanity check to ensure the regex below + # catches all functions! + current_function = None + continue + + # Start of a new function? + mtch = re.match(r"^<(.+)>:$", line) + + if mtch: + # group 0 is always the full match (e.g. in our case the entire string because we have a regex with ^ and $) + # to get the groups defined inside the regex, start at 1. + current_function = Function(mtch.group(1), arch) + functions.append(current_function) + + continue + + # otherwise, must be instruction + if not current_function: + logger.error( + "Unexpectedly found data outside of function. Skipping line %s", line + ) + continue + + current_function.instructions.append(arch.t_instruction.from_str(line)) + + return functions + + +def find_syscalls_in_binary(binary_path: Path): # pylint: disable=too-many-branches + """Statically analyzes the given binary to find all syscalls. + + Uses objdump's '-d' option, parses the output, and then at the call site of each syscall instruction + (and also of simple wrappers around it that weren't inlined during compilation), tries to determine the values + of registers holding arguments to the syscall instruction.""" + if platform.processor() == "x86_64": + arch = ArchitectureX86_64() + else: + arch = ArchitectureAarch64() + + disassembly = subprocess.check_output( + f"objdump --demangle=rust -d {binary_path} --no-show-raw-insn --no-addresses".split() + ).decode("utf-8") + + functions = parse_objdump_output(disassembly, arch) + + found_syscalls = {} + + for fn in functions: + # We don't want to find syscall instruction inside functions that we treat as synthetic syscall instructions + # themselves, because we will not be able to figure out any argument values here (since they are instead + # determined by the arguments to the function itself). Not excluding these would mean the script recognizes + # them as very broad syscall invocations (e.g. only the syscall number is known, but nothing else, meaning + # all seccomp rules that refer to this syscall are more specific and thus cannot be ruled out). + if fn.name in SYSCALL_WRAPPERS or fn.name in SPECIFIC_SYSCALL_WRAPPERS: + continue + + for i, insn in enumerate(fn.instructions): + if insn.is_syscall: + resolved_registers = fn.resolve_registers_before_insn( + i, + [arch.syscall_nr_register] + arch.syscall_argument_registers, + ) + + syscall_nr = resolved_registers.pop(arch.syscall_nr_register) + syscall_args = [ + resolved_registers[reg] for reg in arch.syscall_argument_registers + ] + elif insn.is_call: + # in objdump output, these look like 'call ', so strip the angle brackets + called = insn.args[0][1:-1] + + if called in SYSCALL_WRAPPERS: + resolved_registers = fn.resolve_registers_before_insn( + i, arch.fn_call_argument_registers + ) + + # On x86_64, we are not able to recover the 6th argument passed, since it is passed on the stack + # This is because for the syscall wrapper, the syscall number itself is passed in one of the 6 + # registers available for function arguments in the cdecl convention (instead of being passed in + # eax, which is not usually used for function arguments). + syscall_nr = resolved_registers.pop( + arch.fn_call_argument_registers[0] + ) + syscall_args = [ + resolved_registers[reg] + for reg in arch.fn_call_argument_registers[1:] + ] + elif called in SPECIFIC_SYSCALL_WRAPPERS: + resolved_registers = fn.resolve_registers_before_insn( + i, arch.fn_call_argument_registers + ) + + syscall_nr = seccomp.resolve_syscall( + arch.seccomp_arch, SPECIFIC_SYSCALL_WRAPPERS[called]["syscall"] + ) + syscall_nargs = SPECIFIC_SYSCALL_WRAPPERS[called]["nargs"] + syscall_args = [ + resolved_registers[reg] + for reg in arch.fn_call_argument_registers[:syscall_nargs] + ] + + if all(isinstance(arg, Exception) for arg in syscall_args): + logger.warning( + "Could not resolve any argument for syscall wrapper %s in function %s", + called, + fn.name, + ) + + # If the wrapper performs some transformation of an argument, apply it. + # It'd be cool to determine these automatically via back propagation or something, + # but that's a fairly complex task, and we only have a single syscall wrapper that needs this + for arg, modifier in ( + SPECIFIC_SYSCALL_WRAPPERS[called] + .get("arg_transform", {}) + .items() + ): + syscall_args[arg] = modifier(syscall_args[arg]) + else: + continue + else: + continue + + # This gets triggered in the __lockfile function on x86_64 (syscall number is loader before a branching instruction, + # but if the branch is not taken, linear execution will eventually hit a ret. So during backpropagation we + # would need to skip the section of assembly between "jmp" and "ret", but our script doesn't do anything + # sophisticated like that and thus instead tries to analyse this branch where the syscall number register + # gets clobbered, and it eventually hits a "pop" which it doesnt understand). The syscall in question is + # "futex", and we call that one a million times elsewhere anyway. + # + # See: https://github.com/kraj/musl/blob/ffb23aef7b5339b8c3234f4c6a93c488dc873919/src/stdio/__lockfile.c#L4 + if isinstance(syscall_nr, Exception): + logger.warning( + "Failed to resolve syscall number for instruction %s in function %s: %s", + insn, + fn.name, + syscall_nr, + ) + continue + + syscall_name = seccomp.resolve_syscall( + arch.seccomp_arch, syscall_nr + ).decode("utf-8") + if syscall_name not in found_syscalls: + found_syscalls[syscall_name] = [] + + found_syscalls[syscall_name].append( + [None if isinstance(arg, Exception) else arg for arg in syscall_args] + ) + + return found_syscalls + + +def load_seccomp_rules(seccomp_path: Path): + """Loads seccomp rules from the given file, and presents them as a dictionary + mapping syscalls to a list of individual filters. Each individual filter + describes some restriction of the arguments that are allowed to be passed + to the syscall.""" + filters = json.loads(seccomp_path.read_text("utf-8")) + + all_filters = ( + filters["vcpu"]["filter"] + filters["vmm"]["filter"] + filters["api"]["filter"] + ) + allowlist = defaultdict(list) + + for seccomp_filter in all_filters: + syscall_name = seccomp_filter["syscall"] + + allowlist[syscall_name].append( + {arg["index"]: arg["val"] for arg in seccomp_filter.get("args", [])} + ) + + return allowlist + + +KNOWN_SUPERFLUOUS_RULES = { + # This syscall is inserted at runtime by the linux kernel, and thus not actually present in our binary. + "restart_syscall": [{}] +} + + +def determine_unneeded_seccomp_rules(seccomp_rules, found_syscalls): + """Based on the given list of syscall determined through static analysis, compute which of the + given seccomp rules are redundant. By 'redundant' we here mean that no syscall that would match + it is actually present in the given list of syscalls.""" + + # TODO: We could also determine "too broad" rules here: If all actual invocations of a syscall specific a parameter, + # but the rule does not restrict that parameter, we could recommend to strengthen the rule to specify the parameter! + + redundant_rules = [] + + for syscall, rules in seccomp_rules.items(): + for allowed_arguments in rules: + if ( + syscall in KNOWN_SUPERFLUOUS_RULES + and allowed_arguments in KNOWN_SUPERFLUOUS_RULES[syscall] + ): + continue + + # A rule is not needed if for all actual invocation of the syscall the rule governs, + # the rule does not match. + # Here, we determine "does not match" as "the rule specifies some value for an argument of the syscall to be + # allowed, but the invocation of the syscall never passes this specified value of the argument". + # If there are no invocations of a syscall altogether, then the universal quantification will be vacuously + # true, and any rules involving that syscall are reported as non-needed. + rule_not_needed = all( + any( + actual_invocations[arg_index] is not None + and actual_invocations[arg_index] != allowed_arg + for arg_index, allowed_arg in allowed_arguments.items() + ) + for actual_invocations in found_syscalls.get(syscall, []) + ) + + if rule_not_needed: + redundant_rules.append((syscall, allowed_arguments)) + + return redundant_rules diff --git a/tests/framework/utils.py b/tests/framework/utils.py index a8715f00e94..19f31cf71b0 100644 --- a/tests/framework/utils.py +++ b/tests/framework/utils.py @@ -10,7 +10,6 @@ import re import select import signal -import stat import subprocess import time import typing @@ -29,8 +28,6 @@ wait_fixed, ) -from framework.defs import MIN_KERNEL_VERSION_FOR_IO_URING - FLUSH_CMD = 'screen -S {session} -X colon "logfile flush 0^M"' CommandReturn = namedtuple("CommandReturn", "returncode stdout stderr") CMDLOG = logging.getLogger("commands") @@ -133,70 +130,6 @@ def chroot(path): os.chdir(working_dir) -class UffdHandler: - """Describe the UFFD page fault handler process.""" - - def __init__(self, name, socket_path, mem_file, chroot_path, log_file_name): - """Instantiate the handler process with arguments.""" - self._proc = None - self._handler_name = name - self._socket_path = socket_path - self._mem_file = mem_file - self._chroot = chroot_path - self._log_file = log_file_name - - def spawn(self, uid, gid): - """Spawn handler process using arguments provided.""" - - with chroot(self._chroot): - st = os.stat(self._handler_name) - os.chmod(self._handler_name, st.st_mode | stat.S_IEXEC) - - chroot_log_file = Path("/") / self._log_file - with open(chroot_log_file, "w", encoding="utf-8") as logfile: - args = [f"/{self._handler_name}", self._socket_path, self._mem_file] - self._proc = subprocess.Popen( - args, stdout=logfile, stderr=subprocess.STDOUT - ) - - # Give it time start and fail, if it really has too (bad things happen). - time.sleep(1) - if not self.is_running(): - print(chroot_log_file.read_text(encoding="utf-8")) - assert False, "Could not start PF handler!" - - # The page fault handler will create the socket path with root rights. - # Change rights to the jailer's. - os.chown(self._socket_path, uid, gid) - - @property - def proc(self): - """Return UFFD handler process.""" - return self._proc - - def is_running(self): - """Check if UFFD process is running""" - return self.proc is not None and self.proc.poll() is None - - @property - def log_file(self): - """Return the path to the UFFD handler's log file""" - return Path(self._chroot) / Path(self._log_file) - - @property - def log_data(self): - """Return the log data of the UFFD handler""" - if self.log_file is None: - return "" - return self.log_file.read_text(encoding="utf-8") - - def __del__(self): - """Tear down the UFFD handler process.""" - if self.proc is not None: - self.proc.kill() - - -# pylint: disable=too-few-public-methods class CpuMap: """Cpu map from real cpu cores to containers visible cores. @@ -398,6 +331,12 @@ def run_cmd(cmd, check=False, shell=True, cwd=None, timeout=None) -> CommandRetu stdout, stderr = proc.communicate(timeout=timeout) except subprocess.TimeoutExpired: proc.kill() + + # Sometimes stdout/stderr are passed on to children, in which case killing + # the parent won't close them and communicate will still hang. + proc.stdout.close() + proc.stderr.close() + stdout, stderr = proc.communicate() # Log the message with one call so that multiple statuses @@ -514,17 +453,6 @@ def get_kernel_version(level=2): return linux_version -def is_io_uring_supported(): - """ - Return whether Firecracker supports io_uring for the running kernel ... - - ...version. - """ - kv = packaging.version.parse(get_kernel_version()) - min_kv = packaging.version.parse(MIN_KERNEL_VERSION_FOR_IO_URING) - return kv >= min_kv - - def generate_mmds_session_token(ssh_connection, ipv4_address, token_ttl): """Generate session token used for MMDS V2 requests.""" cmd = "curl -m 2 -s" @@ -655,8 +583,8 @@ class Timeout: """ A Context Manager to timeout sections of code. - >>> with Timeout(30): - >>> time.sleep(35) + >>> with Timeout(30): # doctest: +SKIP + ... time.sleep(35) # doctest: +SKIP """ def __init__(self, seconds, msg="Timed out"): @@ -673,3 +601,8 @@ def __enter__(self): def __exit__(self, _type, _value, _traceback): signal.alarm(0) + + +def pvh_supported() -> bool: + """Checks if PVH boot is supported""" + return platform.architecture() == "x86_64" diff --git a/tests/framework/utils_cpu_templates.py b/tests/framework/utils_cpu_templates.py index 5badd7c640a..0d8f9cd843b 100644 --- a/tests/framework/utils_cpu_templates.py +++ b/tests/framework/utils_cpu_templates.py @@ -3,6 +3,8 @@ """Utilities for CPU template related functionality.""" +# pylint:disable=too-many-return-statements + import json from pathlib import Path @@ -20,16 +22,12 @@ def get_supported_cpu_templates(): - """ - Return the list of CPU templates supported by the platform. - """ - # pylint:disable=too-many-return-statements + """Return the list of static CPU templates supported by the platform.""" host_linux = global_props.host_linux_version_tpl - match get_cpu_vendor(), global_props.cpu_codename: # T2CL template is only supported on Cascade Lake and newer CPUs. case CpuVendor.INTEL, CpuModel.INTEL_SKYLAKE: - return sorted(set(INTEL_TEMPLATES) - set(["T2CL"])) + return sorted(set(INTEL_TEMPLATES) - {"T2CL"}) case CpuVendor.INTEL, _: return INTEL_TEMPLATES case CpuVendor.AMD, _: @@ -42,20 +40,10 @@ def get_supported_cpu_templates(): SUPPORTED_CPU_TEMPLATES = get_supported_cpu_templates() -# Custom CPU templates for Aarch64 for testing -AARCH64_CUSTOM_CPU_TEMPLATES_G2 = ["v1n1"] -AARCH64_CUSTOM_CPU_TEMPLATES_G3 = [ - "aarch64_with_sve_and_pac", - "v1n1", -] - def get_supported_custom_cpu_templates(): - """ - Return the list of custom CPU templates supported by the platform. - """ + """Return the list of custom CPU templates supported by the platform.""" host_linux = global_props.host_linux_version_tpl - match get_cpu_vendor(), global_props.cpu_codename: # T2CL template is only supported on Cascade Lake and newer CPUs. case CpuVendor.INTEL, CpuModel.INTEL_SKYLAKE: @@ -65,9 +53,13 @@ def get_supported_custom_cpu_templates(): case CpuVendor.AMD, _: return AMD_TEMPLATES case CpuVendor.ARM, CpuModel.ARM_NEOVERSE_N1 if host_linux >= (6, 1): - return AARCH64_CUSTOM_CPU_TEMPLATES_G2 + return ["v1n1"] case CpuVendor.ARM, CpuModel.ARM_NEOVERSE_V1 if host_linux >= (6, 1): - return AARCH64_CUSTOM_CPU_TEMPLATES_G3 + return ["v1n1", "aarch64_with_sve_and_pac"] + case CpuVendor.ARM, CpuModel.ARM_NEOVERSE_V1: + return ["aarch64_with_sve_and_pac"] + case CpuVendor.ARM, CpuModel.ARM_NEOVERSE_V2: + return ["aarch64_with_sve_and_pac"] case _: return [] diff --git a/tests/framework/utils_cpuid.py b/tests/framework/utils_cpuid.py index 4303e3ba967..a3988bf7f85 100644 --- a/tests/framework/utils_cpuid.py +++ b/tests/framework/utils_cpuid.py @@ -25,11 +25,14 @@ class CpuModel(str, Enum): """CPU models""" AMD_MILAN = "AMD_MILAN" + AMD_GENOA = "AMD_GENOA" ARM_NEOVERSE_N1 = "ARM_NEOVERSE_N1" ARM_NEOVERSE_V1 = "ARM_NEOVERSE_V1" + ARM_NEOVERSE_V2 = "ARM_NEOVERSE_V2" INTEL_SKYLAKE = "INTEL_SKYLAKE" INTEL_CASCADELAKE = "INTEL_CASCADELAKE" INTEL_ICELAKE = "INTEL_ICELAKE" + INTEL_SAPPHIRE_RAPIDS = "INTEL_SAPPHIRE_RAPIDS" CPU_DICT = { @@ -38,11 +41,14 @@ class CpuModel(str, Enum): "Intel(R) Xeon(R) Platinum 8124M CPU": "INTEL_SKYLAKE", "Intel(R) Xeon(R) Platinum 8259CL CPU": "INTEL_CASCADELAKE", "Intel(R) Xeon(R) Platinum 8375C CPU": "INTEL_ICELAKE", + "Intel(R) Xeon(R) Platinum 8488C": "INTEL_SAPPHIRE_RAPIDS", }, - CpuVendor.AMD: { - "AMD EPYC 7R13": "AMD_MILAN", + CpuVendor.AMD: {"AMD EPYC 7R13": "AMD_MILAN", "AMD EPYC 9R14": "AMD_GENOA"}, + CpuVendor.ARM: { + "0xd0c": "ARM_NEOVERSE_N1", + "0xd40": "ARM_NEOVERSE_V1", + "0xd4f": "ARM_NEOVERSE_V2", }, - CpuVendor.ARM: {"0xd0c": "ARM_NEOVERSE_N1", "0xd40": "ARM_NEOVERSE_V1"}, } @@ -79,6 +85,8 @@ def get_cpu_codename(default="Unknown"): result = re.match(r"^(.*) @.*$", cpu_model) if result: return CPU_DICT[CpuVendor.INTEL].get(result.group(1), default) + # Some Intel CPUs (e.g. Intel Sapphire Rapids) don't include "@ ". + return CPU_DICT[CpuVendor.INTEL].get(cpu_model, default) if vendor == CpuVendor.AMD: result = re.match(r"^(.*) [0-9]*-Core Processor$", cpu_model) if result: diff --git a/tests/framework/utils_drive.py b/tests/framework/utils_drive.py index 2e67ff41f39..7bb623d73e0 100644 --- a/tests/framework/utils_drive.py +++ b/tests/framework/utils_drive.py @@ -23,17 +23,15 @@ class VhostUserBlkBackendType(Enum): CROSVM = "Crosvm" -def partuuid_and_disk_path(rootfs_ubuntu_22, disk_path): +def partuuid_and_disk_path(rootfs, disk_path): """ We create a new file with specified path, get its partuuid and use it as a rootfs. """ - initial_size = rootfs_ubuntu_22.stat().st_size + 50 * MB + initial_size = rootfs.stat().st_size + 50 * MB disk_path.touch() os.truncate(disk_path, initial_size) check_output(f"echo type=83 | sfdisk --no-tell-kernel {str(disk_path)}", shell=True) - check_output( - f"dd bs=1M seek=1 if={str(rootfs_ubuntu_22)} of={disk_path}", shell=True - ) + check_output(f"dd bs=1M seek=1 if={str(rootfs)} of={disk_path}", shell=True) ptuuid = check_output( f"blkid -s PTUUID -o value {disk_path}", shell=True, encoding="ascii" ).strip() diff --git a/tests/framework/utils_imdsv2.py b/tests/framework/utils_imdsv2.py index 6420f7f9acf..7a6a89b58a5 100644 --- a/tests/framework/utils_imdsv2.py +++ b/tests/framework/utils_imdsv2.py @@ -24,7 +24,8 @@ class IMDSv2Client: """ A simple IMDSv2 client. - >>> IMDSv2Client().get("/meta-data/instance-type") + >>> IMDSv2Client().get("/meta-data/instance-type") # doctest: +SKIP + ... """ def __init__(self, endpoint="http://169.254.169.254", version="latest"): @@ -49,8 +50,8 @@ def get(self, path): """ Get a metadata path from IMDSv2 - >>> IMDSv2Client().get("/meta-data/instance-type") - >>> m5d.metal + >>> IMDSv2Client().get("/meta-data/instance-type") # doctest: +SKIP + 'm5d.metal' """ headers = {IMDSV2_HDR_TOKEN: self.get_token()} url = f"{self.endpoint}/{self.version}{path}" diff --git a/tests/framework/utils_uffd.py b/tests/framework/utils_uffd.py new file mode 100644 index 00000000000..b25ca6cd928 --- /dev/null +++ b/tests/framework/utils_uffd.py @@ -0,0 +1,105 @@ +# Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""UFFD related utility functions""" + +import os +import stat +import subprocess +import time +from pathlib import Path + +from framework.utils import chroot +from host_tools import cargo_build + +SOCKET_PATH = "/firecracker-uffd.sock" + + +class UffdHandler: + """Describe the UFFD page fault handler process.""" + + def __init__( + self, name, socket_path, snapshot: "Snapshot", chroot_path, log_file_name + ): + """Instantiate the handler process with arguments.""" + self._proc = None + self._handler_name = name + self.socket_path = socket_path + self.snapshot = snapshot + self._chroot = chroot_path + self._log_file = log_file_name + + def spawn(self, uid, gid): + """Spawn handler process using arguments provided.""" + + with chroot(self._chroot): + st = os.stat(self._handler_name) + os.chmod(self._handler_name, st.st_mode | stat.S_IEXEC) + + chroot_log_file = Path("/") / self._log_file + with open(chroot_log_file, "w", encoding="utf-8") as logfile: + args = [ + f"/{self._handler_name}", + self.socket_path, + self.snapshot.mem.name, + ] + self._proc = subprocess.Popen( + args, stdout=logfile, stderr=subprocess.STDOUT + ) + + # Give it time start and fail, if it really has too (bad things happen). + time.sleep(1) + if not self.is_running(): + print(chroot_log_file.read_text(encoding="utf-8")) + assert False, "Could not start PF handler!" + + # The page fault handler will create the socket path with root rights. + # Change rights to the jailer's. + os.chown(self.socket_path, uid, gid) + + @property + def proc(self): + """Return UFFD handler process.""" + return self._proc + + def is_running(self): + """Check if UFFD process is running""" + return self.proc is not None and self.proc.poll() is None + + @property + def log_file(self): + """Return the path to the UFFD handler's log file""" + return Path(self._chroot) / Path(self._log_file) + + @property + def log_data(self): + """Return the log data of the UFFD handler""" + if self.log_file is None: + return "" + return self.log_file.read_text(encoding="utf-8") + + def __del__(self): + """Tear down the UFFD handler process.""" + if self.proc is not None: + self.proc.kill() + + +def spawn_pf_handler(vm, handler_path, snapshot): + """Spawn page fault handler process.""" + # Copy snapshot memory file into chroot of microVM. + jailed_snapshot = snapshot.copy_to_chroot(Path(vm.chroot())) + # Copy the valid page fault binary into chroot of microVM. + jailed_handler = vm.create_jailed_resource(handler_path) + handler_name = os.path.basename(jailed_handler) + + uffd_handler = UffdHandler( + handler_name, SOCKET_PATH, jailed_snapshot, vm.chroot(), "uffd.log" + ) + uffd_handler.spawn(vm.jailer.uid, vm.jailer.gid) + vm.uffd_handler = uffd_handler + + return uffd_handler + + +def uffd_handler(handler_name, **kwargs): + """Retrieves the uffd handler with the given name""" + return cargo_build.get_example(f"uffd_{handler_name}_handler", **kwargs) diff --git a/tests/framework/utils_vsock.py b/tests/framework/utils_vsock.py index 3f6885e3afd..9561c1c26f2 100644 --- a/tests/framework/utils_vsock.py +++ b/tests/framework/utils_vsock.py @@ -11,6 +11,8 @@ from subprocess import Popen from threading import Thread +from tenacity import Retrying, stop_after_attempt, wait_fixed + ECHO_SERVER_PORT = 5252 SERVER_ACCEPT_BACKLOG = 128 TEST_CONNECTION_COUNT = 50 @@ -142,53 +144,57 @@ def check_guest_connections(vm, server_port_path, blob_path, blob_hash): ["socat", f"UNIX-LISTEN:{server_port_path},fork,backlog=5", "exec:'/bin/cat'"] ) - # Link the listening Unix socket into the VM's jail, so that - # Firecracker can connect to it. - attempt = 0 - # But 1st, give socat a bit of time to create the socket - while not Path(server_port_path).exists() and attempt < 3: - time.sleep(0.2) - attempt += 1 - vm.create_jailed_resource(server_port_path) - - # Increase maximum process count for the ssh service. - # Avoids: "bash: fork: retry: Resource temporarily unavailable" - # Needed to execute the bash script that tests for concurrent - # vsock guest initiated connections. - pids_max_file = "/sys/fs/cgroup/system.slice/ssh.service/pids.max" - ecode, _, _ = vm.ssh.run(f"echo 1024 > {pids_max_file}") - assert ecode == 0, "Unable to set max process count for guest ssh service." - - # Build the guest worker sub-command. - # `vsock_helper` will read the blob file from STDIN and send the echo - # server response to STDOUT. This response is then hashed, and the - # hash is compared against `blob_hash` (computed on the host). This - # comparison sets the exit status of the worker command. - worker_cmd = "hash=$(" - worker_cmd += "cat {}".format(blob_path) - worker_cmd += " | /tmp/vsock_helper echo 2 {}".format(ECHO_SERVER_PORT) - worker_cmd += " | md5sum | cut -f1 -d\\ " - worker_cmd += ")" - worker_cmd += ' && [[ "$hash" = "{}" ]]'.format(blob_hash) - - # Run `TEST_CONNECTION_COUNT` concurrent workers, using the above - # worker sub-command. - # If any worker fails, this command will fail. If all worker sub-commands - # succeed, this will also succeed. - cmd = 'workers="";' - cmd += "for i in $(seq 1 {}); do".format(TEST_CONNECTION_COUNT) - cmd += " ({})& ".format(worker_cmd) - cmd += ' workers="$workers $!";' - cmd += "done;" - cmd += "for w in $workers; do wait $w || (wait; exit 1); done" - - ecode, _, stderr = vm.ssh.run(cmd) - echo_server.terminate() - rc = echo_server.wait() - # socat exits with 128 + 15 (SIGTERM) - assert rc == 143 - - assert ecode == 0, stderr + try: + # Give socat a bit of time to create the socket + for attempt in Retrying( + wait=wait_fixed(0.2), + stop=stop_after_attempt(3), + reraise=True, + ): + with attempt: + assert Path(server_port_path).exists() + + # Link the listening Unix socket into the VM's jail, so that + # Firecracker can connect to it. + vm.create_jailed_resource(server_port_path) + + # Increase maximum process count for the ssh service. + # Avoids: "bash: fork: retry: Resource temporarily unavailable" + # Needed to execute the bash script that tests for concurrent + # vsock guest initiated connections. + vm.ssh.check_output( + "echo 1024 > /sys/fs/cgroup/system.slice/ssh.service/pids.max" + ) + + # Build the guest worker sub-command. + # `vsock_helper` will read the blob file from STDIN and send the echo + # server response to STDOUT. This response is then hashed, and the + # hash is compared against `blob_hash` (computed on the host). This + # comparison sets the exit status of the worker command. + worker_cmd = "hash=$(" + worker_cmd += "cat {}".format(blob_path) + worker_cmd += " | /tmp/vsock_helper echo 2 {}".format(ECHO_SERVER_PORT) + worker_cmd += " | md5sum | cut -f1 -d\\ " + worker_cmd += ")" + worker_cmd += ' && [[ "$hash" = "{}" ]]'.format(blob_hash) + + # Run `TEST_CONNECTION_COUNT` concurrent workers, using the above + # worker sub-command. + # If any worker fails, this command will fail. If all worker sub-commands + # succeed, this will also succeed. + cmd = 'workers="";' + cmd += "for i in $(seq 1 {}); do".format(TEST_CONNECTION_COUNT) + cmd += " ({})& ".format(worker_cmd) + cmd += ' workers="$workers $!";' + cmd += "done;" + cmd += "for w in $workers; do wait $w || (wait; exit 1); done" + + vm.ssh.check_output(cmd) + finally: + echo_server.terminate() + rc = echo_server.wait() + # socat exits with 128 + 15 (SIGTERM) + assert rc == 143 def make_host_port_path(uds_path, port): diff --git a/tests/host_tools/cargo_build.py b/tests/host_tools/cargo_build.py index da72e5433b2..56af399dea7 100644 --- a/tests/host_tools/cargo_build.py +++ b/tests/host_tools/cargo_build.py @@ -7,13 +7,18 @@ from pathlib import Path from framework import defs, utils -from framework.defs import FC_WORKSPACE_DIR +from framework.defs import DEFAULT_BINARY_DIR from framework.with_filelock import with_filelock DEFAULT_TARGET = f"{platform.machine()}-unknown-linux-musl" DEFAULT_TARGET_DIR = f"{DEFAULT_TARGET}/release/" +def nightly_toolchain() -> str: + """Receives the name of the installed nightly toolchain""" + return utils.check_output("rustup toolchain list | grep nightly").stdout.strip() + + def cargo( subcommand, cargo_args: str = "", @@ -21,11 +26,15 @@ def cargo( *, env: dict = None, cwd: str = None, + nightly: bool = False, ): """Executes the specified cargo subcommand""" + toolchain = f"+{nightly_toolchain()}" if nightly else "" env = env or {} env_string = " ".join(f'{key}="{str(value)}"' for key, value in env.items()) - cmd = f"{env_string} cargo {subcommand} {cargo_args} -- {subcommand_args}" + cmd = ( + f"{env_string} cargo {toolchain} {subcommand} {cargo_args} -- {subcommand_args}" + ) return utils.check_output(cmd, cwd=cwd) @@ -47,26 +56,14 @@ def cargo_test(path, extra_args=""): cargo("test", extra_args + " --all --no-fail-fast", env=env) -def get_binary(name, *, workspace_dir=FC_WORKSPACE_DIR, example=None): +def get_binary(name, *, binary_dir=DEFAULT_BINARY_DIR, example=None): """Get a binary. The binaries are built before starting a testrun.""" - target_dir = workspace_dir / "build" / "cargo_target" / DEFAULT_TARGET_DIR - bin_path = target_dir / name + bin_path = binary_dir / name if example: - bin_path = target_dir / "examples" / example + bin_path = binary_dir / "examples" / example return bin_path -def get_firecracker_binaries(*, workspace_dir=FC_WORKSPACE_DIR): - """Build the Firecracker and Jailer binaries if they don't exist. - - Returns the location of the firecracker related binaries eventually after - building them in case they do not exist at the specified root_path. - """ - return get_binary("firecracker", workspace_dir=workspace_dir), get_binary( - "jailer", workspace_dir=workspace_dir - ) - - def get_example(name, *args, package="firecracker", **kwargs): """Build an example binary""" return get_binary(package, *args, **kwargs, example=name) diff --git a/tests/host_tools/fcmetrics.py b/tests/host_tools/fcmetrics.py index e33e89089dc..47661d5b27d 100644 --- a/tests/host_tools/fcmetrics.py +++ b/tests/host_tools/fcmetrics.py @@ -472,6 +472,7 @@ def flatten_dict(node, prefix: str): # See also https://github.com/stefano-garzarella/iperf-vsock/issues/4 "fc_metrics.vsock.rx_read_fails", "fc_metrics.vsock.tx_write_fails", + "fc_metrics.vsock.tx_flush_fails", ] failure_metrics = { diff --git a/tests/host_tools/memory.py b/tests/host_tools/memory.py index 690cda38701..93380a9321d 100644 --- a/tests/host_tools/memory.py +++ b/tests/host_tools/memory.py @@ -62,7 +62,7 @@ def run(self): guest_mem_bytes = self._vm.mem_size_bytes try: ps = psutil.Process(self._vm.firecracker_pid) - except psutil.NoSuchProcess: + except (psutil.NoSuchProcess, FileNotFoundError): return while not self._should_stop: try: diff --git a/tests/host_tools/network.py b/tests/host_tools/network.py index e1c53020fd0..93cdb323c50 100644 --- a/tests/host_tools/network.py +++ b/tests/host_tools/network.py @@ -3,29 +3,35 @@ """Utilities for test host microVM network setup.""" import ipaddress +import os import random +import re +import signal import string from dataclasses import dataclass, field from pathlib import Path -from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed +from tenacity import retry, stop_after_attempt, wait_fixed from framework import utils +from framework.utils import Timeout class SSHConnection: """ SSHConnection encapsulates functionality for microVM SSH interaction. - This class should be instantiated as part of the ssh fixture with the + This class should be instantiated as part of the ssh fixture with the hostname obtained from the MAC address, the username for logging into the image and the path of the ssh key. - This translates into an SSH connection as follows: - ssh -i ssh_key_path username@hostname + Establishes a ControlMaster upon construction, which is then re-used + for all subsequent SSH interactions. """ - def __init__(self, netns, ssh_key: Path, host, user, *, on_error=None): + def __init__( + self, netns, ssh_key: Path, control_path: Path, host, user, *, on_error=None + ): """Instantiate a SSH client and connect to a microVM.""" self.netns = netns self.ssh_key = ssh_key @@ -36,22 +42,13 @@ def __init__(self, netns, ssh_key: Path, host, user, *, on_error=None): assert (ssh_key.stat().st_mode & 0o777) == 0o400 self.host = host self.user = user + self._control_path = control_path self._on_error = None self.options = [ "-o", - "LogLevel=ERROR", - "-o", - "ConnectTimeout=1", - "-o", - "StrictHostKeyChecking=no", - "-o", - "UserKnownHostsFile=/dev/null", - "-o", - "PreferredAuthentications=publickey", - "-i", - str(self.ssh_key), + f"ControlPath={self._control_path}", ] # _init_connection loops until it can connect to the guest @@ -67,9 +64,14 @@ def __init__(self, netns, ssh_key: Path, host, user, *, on_error=None): self._on_error = on_error + @property + def user_host(self): + """remote address for in SSH format @""" + return f"{self.user}@{self.host}" + def remote_path(self, path): """Convert a path to remote""" - return f"{self.user}@{self.host}:{path}" + return f"{self.user_host}:{path}" def _scp(self, path1, path2, options): """Copy files to/from the VM using scp.""" @@ -90,48 +92,110 @@ def scp_get(self, remote_path, local_path, recursive=False): self._scp(self.remote_path(remote_path), local_path, opts) @retry( - retry=retry_if_exception_type(ChildProcessError), - wait=wait_fixed(0.5), + wait=wait_fixed(1), stop=stop_after_attempt(20), reraise=True, ) def _init_connection(self): - """Create an initial SSH client connection (retry until it works). + """Initialize the persistent background connection which will be used + to execute all commands sent via this `SSHConnection` object. Since we're connecting to a microVM we just started, we'll probably have to wait for it to boot up and start the SSH server. We'll keep trying to execute a remote command that can't fail (`/bin/true`), until we get a successful (0) exit code. """ - self.check_output("true", timeout=100, debug=True) + assert not self._control_path.exists() + + # Sadly, we cannot get debug output from this command (e.g. `-vvv`), + # because passing -vvv causes the daemonized ssh to hold on to stderr, + # and inside utils.run_cmd we're using subprocess.communicate, which + # only returns once stderr gets closed (which would thus result in an + # indefinite hang). + establish_cmd = [ + "ssh", + # Only need to pass the ssh key here, as all multiplexed + # connections won't have to re-authenticate + "-i", + str(self.ssh_key), + "-o", + "StrictHostKeyChecking=no", + "-o", + "ConnectTimeout=2", + # Set up a persistent background connection + "-o", + "ControlMaster=auto", + "-o", + "ControlPersist=yes", + *self.options, + self.user_host, + "true", + ] + + try: + # don't set a low timeout here, because otherwise we might get into a race condition + # where ssh already forked off the persisted connection daemon, but gets killed here + # before exiting itself. In that case, self._control_path will exist, and the retry + # will hit the assert at the start of this function. + self._exec(establish_cmd, check=True) + except Exception: + # if the control socket is present, then the daemon is running, and we should stop it + # before retrying again + if self._control_path.exists(): + self.close() + raise + + def _check_liveness(self) -> int: + """Checks whether the ControlPersist connection is still alive""" + check_cmd = ["ssh", "-O", "check", *self.options, self.user_host] - def run(self, cmd_string, timeout=None, *, check=False, debug=False): + _, _, stderr = self._exec(check_cmd, check=True) + + pid_match = re.match(r"Master running \(pid=(\d+)\)", stderr) + + assert pid_match, f"SSH ControlMaster connection not alive anymore: {stderr}" + + return int(pid_match.group(1)) + + def close(self): + """Closes the ControlPersist connection""" + master_pid = self._check_liveness() + + stop_cmd = ["ssh", "-O", "stop", *self.options, self.user_host] + + _, _, stderr = self._exec(stop_cmd, check=True) + + assert "Stop listening request sent" in stderr + + try: + with Timeout(5): + utils.wait_process_termination(master_pid) + except TimeoutError: + # for some reason it won't exit, let's force it... + # if this also fails, when during teardown we'll get an error about + # "found a process with supposedly dead Firecracker's jailer ID" + os.kill(master_pid, signal.SIGKILL) + + def run(self, cmd_string, timeout=100, *, check=False, debug=False): """ Execute the command passed as a string in the ssh context. If `debug` is set, pass `-vvv` to `ssh`. Note that this will clobber stderr. """ - command = [ - "ssh", - *self.options, - f"{self.user}@{self.host}", - cmd_string, - ] + self._check_liveness() + + command = ["ssh", *self.options, self.user_host, cmd_string] if debug: command.insert(1, "-vvv") - return self._exec( - command, - timeout, - check=check, - ) + return self._exec(command, timeout, check=check) - def check_output(self, cmd_string, timeout=None, *, debug=False): + def check_output(self, cmd_string, timeout=100, *, debug=False): """Same as `run`, but raises an exception on non-zero return code of remote command""" return self.run(cmd_string, timeout, check=True, debug=debug) - def _exec(self, cmd, timeout=None, check=False): + def _exec(self, cmd, timeout=100, check=False): """Private function that handles the ssh client invocation.""" if self.netns is not None: cmd = ["ip", "netns", "exec", self.netns] + cmd @@ -187,15 +251,13 @@ def __init__(self, name, netns, ip=None): It also creates a new tap device, brings it up and moves the interface to the specified namespace. """ - # Avoid a conflict if two tests want to create the same tap device tap0 - # in the host before moving it into its own netns - temp_name = "tap" + random_str(k=8) - utils.check_output(f"ip tuntap add mode tap name {temp_name}") - utils.check_output(f"ip link set {temp_name} name {name} netns {netns}") - if ip: - utils.check_output(f"ip netns exec {netns} ifconfig {name} {ip} up") self._name = name self._netns = netns + # Create the tap device tap0 directly in the network namespace to avoid + # conflicts + self.netns.check_output(f"ip tuntap add mode tap name {name}") + if ip: + self.netns.check_output(f"ifconfig {name} {ip} up") @property def name(self): @@ -209,14 +271,10 @@ def netns(self): def set_tx_queue_len(self, tx_queue_len): """Set the length of the tap's TX queue.""" - utils.check_output( - "ip netns exec {} ip link set {} txqueuelen {}".format( - self.netns, self.name, tx_queue_len - ) - ) + self.netns.check_output(f"ip link set {self.name} txqueuelen {tx_queue_len}") def __repr__(self): - return f"" + return f"" @dataclass(frozen=True, repr=True) @@ -251,7 +309,7 @@ def with_id(i, netmask_len=30): ) -@dataclass(frozen=True, repr=True) +@dataclass(repr=True) class NetNs: """Defines a network namespace.""" @@ -270,6 +328,10 @@ def cmd_prefix(self): """Return the jailer context netns file prefix.""" return f"ip netns exec {self.id}" + def check_output(self, cmd: str): + """Run a command inside the netns.""" + return utils.check_output(f"{self.cmd_prefix()} {cmd}") + def setup(self): """Set up this network namespace.""" if not self.path.exists(): @@ -286,6 +348,19 @@ def add_tap(self, name, ip): We assume that a Tap is always configured with the same IP. """ if name not in self.taps: - tap = Tap(name, self.id, ip) + tap = Tap(name, self, ip) self.taps[name] = tap return self.taps[name] + + def is_used(self): + """Are any of the TAPs still in use + + Waits until there's no carrier signal. + Otherwise trying to reuse the TAP may return + `Resource busy (os error 16)` + """ + for tap in self.taps: + _, stdout, _ = self.check_output(f"cat /sys/class/net/{tap}/carrier") + if stdout.strip() != "0": + return True + return False diff --git a/tests/host_tools/test_syscalls.c b/tests/host_tools/test_syscalls.c new file mode 100644 index 00000000000..685e45acde0 --- /dev/null +++ b/tests/host_tools/test_syscalls.c @@ -0,0 +1,76 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// This is used by `test_seccomp_validate.py` + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +void install_bpf_filter(char *bpf_file) { + int fd = open(bpf_file, O_RDONLY); + if (fd == -1) { + perror("open"); + exit(EXIT_FAILURE); + } + struct stat sb; + if (fstat(fd, &sb) == -1) { + perror("stat"); + exit(EXIT_FAILURE); + } + size_t size = sb.st_size; + struct sock_filter *filterbuf = (struct sock_filter*)malloc(size); + if (read(fd, filterbuf, size) == -1) { + perror("read"); + exit(EXIT_FAILURE); + } + + /* Install seccomp filter */ + size_t insn_len = size / sizeof(struct sock_filter); + struct sock_fprog prog = { + .len = (unsigned short)(insn_len), + .filter = filterbuf, + }; + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + perror("prctl(NO_NEW_PRIVS)"); + exit(EXIT_FAILURE); + } + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { + perror("prctl(SECCOMP)"); + exit(EXIT_FAILURE); + } +} + + +int main(int argc, char **argv) { + /* parse arguments */ + if (argc < 3) { + fprintf(stderr, "Usage: %s BPF_FILE ARG0..\n", argv[0]); + exit(EXIT_FAILURE); + } + char *bpf_file = argv[1]; + long syscall_id = atoi(argv[2]); + long arg0, arg1, arg2, arg3; + arg0 = arg1 = arg2 = arg3 = 0L; + if (argc > 3) arg0 = atol(argv[3]); + if (argc > 4) arg1 = atol(argv[4]); + if (argc > 5) arg2 = atol(argv[5]); + if (argc > 6) arg3 = atol(argv[6]); + + /* read seccomp filter from file */ + if (strcmp(bpf_file, "/dev/null") != 0) { + install_bpf_filter(bpf_file); + } + + long res = syscall(syscall_id, arg0, arg1, arg2, arg3); + return EXIT_SUCCESS; +} diff --git a/tests/host_tools/udp_offload.py b/tests/host_tools/udp_offload.py index e9ab6a93966..e105c8e08bd 100644 --- a/tests/host_tools/udp_offload.py +++ b/tests/host_tools/udp_offload.py @@ -24,35 +24,37 @@ def eprint(*args, **kwargs): SOL_UDP = 17 # Protocol number for UDP UDP_SEGMENT = 103 # Option code for UDP segmentation (non-standard) -# Get the IP and port from command-line arguments -if len(sys.argv) != 3: - eprint("Usage: python3 udp_offload.py ") - sys.exit(1) -ip_address = sys.argv[1] -port = int(sys.argv[2]) - -# Create a UDP socket -sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - -# Set the UDP segmentation option (UDP_SEGMENT) to 1400 bytes -OPTVAL = 1400 -try: - sockfd.setsockopt(SOL_UDP, UDP_SEGMENT, OPTVAL) -except (AttributeError, PermissionError): - eprint("Unable to set UDP_SEGMENT option") - sys.exit(1) - -# Set the destination address and port -servaddr = (ip_address, port) - -# Send the message to the destination address -MESSAGE = b"x" -try: - sockfd.sendto(MESSAGE, servaddr) - print("Message sent successfully") -except socket.error as e: - eprint(f"Error sending message: {e}") - sys.exit(1) - -sockfd.close() +if __name__ == "__main__": + # Get the IP and port from command-line arguments + if len(sys.argv) != 3: + eprint("Usage: python3 udp_offload.py ") + sys.exit(1) + + ip_address = sys.argv[1] + port = int(sys.argv[2]) + + # Create a UDP socket + sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + # Set the UDP segmentation option (UDP_SEGMENT) to 1400 bytes + OPTVAL = 1400 + try: + sockfd.setsockopt(SOL_UDP, UDP_SEGMENT, OPTVAL) + except (AttributeError, PermissionError): + eprint("Unable to set UDP_SEGMENT option") + sys.exit(1) + + # Set the destination address and port + servaddr = (ip_address, port) + + # Send the message to the destination address + MESSAGE = b"x" + try: + sockfd.sendto(MESSAGE, servaddr) + print("Message sent successfully") + except socket.error as e: + eprint(f"Error sending message: {e}") + sys.exit(1) + + sockfd.close() diff --git a/tests/integration_tests/build/test_coverage.py b/tests/integration_tests/build/test_coverage.py index 9369498b377..d8ce67fa6d7 100644 --- a/tests/integration_tests/build/test_coverage.py +++ b/tests/integration_tests/build/test_coverage.py @@ -63,7 +63,7 @@ def test_coverage(monkeypatch): --ignore "**/test_utils*" \ --ignore "**/mock_*" \ --ignore "src/firecracker/examples/*" \ - --ignore "**/gen*" \ + --ignore "**/generated*" \ -t lcov \ --ignore-not-existing \ -o {lcov_file}""" @@ -95,7 +95,9 @@ def test_coverage(monkeypatch): if not branch: branch = utils.check_output("git rev-parse --abbrev-ref HEAD").stdout - codecov_cmd = f"codecov -f {lcov_file} -F {global_props.host_linux_version}-{global_props.instance}" + # -Z flag means "fail on error". There's supposed to be a more descriptive long form in + # --fail-on-error, but it doesnt work. + codecov_cmd = f"codecov -Z -f {lcov_file} -F {global_props.host_linux_version}-{global_props.instance}" if pr_number and pr_number != "false": codecov_cmd += f" -P {pr_number}" diff --git a/tests/integration_tests/build/test_dependencies.py b/tests/integration_tests/build/test_dependencies.py new file mode 100644 index 00000000000..6ee3a675702 --- /dev/null +++ b/tests/integration_tests/build/test_dependencies.py @@ -0,0 +1,12 @@ +# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Enforces controls over dependencies.""" + +from host_tools.cargo_build import cargo + + +def test_unused_dependencies(): + """ + Test that there are no unused dependencies. + """ + cargo("udeps", "--all", nightly=True) diff --git a/tests/integration_tests/build/test_seccomp_no_redundant_rules.py b/tests/integration_tests/build/test_seccomp_no_redundant_rules.py new file mode 100644 index 00000000000..a61b86b5547 --- /dev/null +++ b/tests/integration_tests/build/test_seccomp_no_redundant_rules.py @@ -0,0 +1,37 @@ +# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""A test that fails if it can definitely prove a seccomp rule redundant +(although it passing does not guarantee the converse, that all rules are definitely needed).""" +import platform +from pathlib import Path + +from framework import utils +from framework.static_analysis import ( + determine_unneeded_seccomp_rules, + find_syscalls_in_binary, + load_seccomp_rules, +) + + +def test_redundant_seccomp_rules(): + """Test that fails if static analysis determines redundant seccomp rules""" + arch = platform.processor() + + nightly_toolchain = utils.check_output( + "rustup toolchain list | grep nightly" + ).stdout.strip() + target = f"{arch}-unknown-linux-musl" + + utils.check_output( + f'RUSTFLAGS="-C relocation-model=static -C link-args=-no-pie" cargo +{nightly_toolchain} -Zbuild-std=panic_abort,std build --release --target {target} -p firecracker' + ) + + found_syscalls = find_syscalls_in_binary( + Path(f"../build/cargo_target/{target}/release/firecracker") + ) + + seccomp_rules = load_seccomp_rules(Path(f"../resources/seccomp/{target}.json")) + + redundant_rules = determine_unneeded_seccomp_rules(seccomp_rules, found_syscalls) + + assert not redundant_rules, f"Found redundant seccomp rules! {redundant_rules}" diff --git a/tests/integration_tests/functional/test_api.py b/tests/integration_tests/functional/test_api.py index 5aebe7b5265..eb2cd1b608e 100644 --- a/tests/integration_tests/functional/test_api.py +++ b/tests/integration_tests/functional/test_api.py @@ -16,8 +16,8 @@ import host_tools.drive as drive_tools import host_tools.network as net_tools -from framework import utils_cpuid -from framework.utils import get_firecracker_version_from_toml, is_io_uring_supported +from framework import utils, utils_cpuid +from framework.utils import get_firecracker_version_from_toml MEM_LIMIT = 1000000000 @@ -42,8 +42,11 @@ def test_api_happy_start(uvm_plain): test_microvm.start() + if utils.pvh_supported(): + assert "Kernel loaded using PVH boot protocol" in test_microvm.log_data -def test_drive_io_engine(uvm_plain): + +def test_drive_io_engine(uvm_plain, io_engine): """ Test io_engine configuration. @@ -56,8 +59,6 @@ def test_drive_io_engine(uvm_plain): test_microvm.basic_config(add_root_device=False) test_microvm.add_net_iface() - supports_io_uring = is_io_uring_supported() - kwargs = { "drive_id": "rootfs", "path_on_host": test_microvm.create_jailed_resource(test_microvm.rootfs_file), @@ -65,26 +66,13 @@ def test_drive_io_engine(uvm_plain): "is_read_only": True, } - # Test the opposite of the default backend type. - if supports_io_uring: - test_microvm.api.drive.put(io_engine="Sync", **kwargs) - - if not supports_io_uring: - with pytest.raises(RuntimeError): - test_microvm.api.drive.put(io_engine="Async", **kwargs) - # The Async engine is not supported for older kernels. - test_microvm.check_log_message( - "Received Error. Status code: 400 Bad Request. Message: Drive config error: " - "Unable to create the virtio block device: Virtio backend error: " - "Error coming from the IO engine: Unsupported engine type: Async" - ) - - # Now configure the default engine type and check that it works. - test_microvm.api.drive.put(**kwargs) + test_microvm.api.drive.put(io_engine=io_engine, **kwargs) test_microvm.start() - assert test_microvm.api.vm_config.get().json()["drives"][0]["io_engine"] == "Sync" + assert ( + test_microvm.api.vm_config.get().json()["drives"][0]["io_engine"] == io_engine + ) def test_api_put_update_pre_boot(uvm_plain, io_engine): @@ -101,7 +89,7 @@ def test_api_put_update_pre_boot(uvm_plain, io_engine): test_microvm.basic_config() fs1 = drive_tools.FilesystemFile(os.path.join(test_microvm.fsfiles, "scratch")) - response = test_microvm.api.drive.put( + test_microvm.api.drive.put( drive_id="scratch", path_on_host=test_microvm.create_jailed_resource(fs1.path), is_root_device=False, @@ -191,15 +179,15 @@ def test_net_api_put_update_pre_boot(uvm_plain): test_microvm = uvm_plain test_microvm.spawn() - first_if_name = "first_tap" - tap1 = net_tools.Tap(first_if_name, test_microvm.netns.id) + tap1name = test_microvm.id[:8] + "tap1" + tap1 = net_tools.Tap(tap1name, test_microvm.netns) test_microvm.api.network.put( iface_id="1", guest_mac="06:00:00:00:00:01", host_dev_name=tap1.name ) # Adding new network interfaces is allowed. - second_if_name = "second_tap" - tap2 = net_tools.Tap(second_if_name, test_microvm.netns.id) + tap2name = test_microvm.id[:8] + "tap2" + tap2 = net_tools.Tap(tap2name, test_microvm.netns) test_microvm.api.network.put( iface_id="2", guest_mac="07:00:00:00:00:01", host_dev_name=tap2.name ) @@ -209,28 +197,26 @@ def test_net_api_put_update_pre_boot(uvm_plain): expected_msg = f"The MAC address is already in use: {guest_mac}" with pytest.raises(RuntimeError, match=expected_msg): test_microvm.api.network.put( - iface_id="2", host_dev_name=second_if_name, guest_mac=guest_mac + iface_id="2", host_dev_name=tap2name, guest_mac=guest_mac ) # Updates to a network interface with an available MAC are allowed. test_microvm.api.network.put( - iface_id="2", host_dev_name=second_if_name, guest_mac="08:00:00:00:00:01" + iface_id="2", host_dev_name=tap2name, guest_mac="08:00:00:00:00:01" ) # Updates to a network interface with an unavailable name are not allowed. expected_msg = "Could not create the network device" with pytest.raises(RuntimeError, match=expected_msg): test_microvm.api.network.put( - iface_id="1", host_dev_name=second_if_name, guest_mac="06:00:00:00:00:01" + iface_id="1", host_dev_name=tap2name, guest_mac="06:00:00:00:00:01" ) # Updates to a network interface with an available name are allowed. - iface_id = "1" - tapname = test_microvm.id[:8] + "tap" + iface_id - - tap3 = net_tools.Tap(tapname, test_microvm.netns.id) + tap3name = test_microvm.id[:8] + "tap3" + tap3 = net_tools.Tap(tap3name, test_microvm.netns) test_microvm.api.network.put( - iface_id=iface_id, host_dev_name=tap3.name, guest_mac="06:00:00:00:00:01" + iface_id="3", host_dev_name=tap3.name, guest_mac="06:00:00:00:00:01" ) @@ -252,7 +238,7 @@ def test_api_mmds_config(uvm_plain): "The list of network interface IDs that allow " "forwarding MMDS requests is empty." ) - with pytest.raises(RuntimeError): + with pytest.raises(RuntimeError, match=err_msg): test_microvm.api.mmds_config.put(network_interfaces=[]) # Setting MMDS config when no network device has been attached @@ -266,7 +252,7 @@ def test_api_mmds_config(uvm_plain): test_microvm.api.mmds_config.put(network_interfaces=["foo"]) # Attach network interface. - tap = net_tools.Tap("tap1", test_microvm.netns.id) + tap = net_tools.Tap(f"tap1-{test_microvm.id[:6]}", test_microvm.netns) test_microvm.api.network.put( iface_id="1", guest_mac="06:00:00:00:00:01", host_dev_name=tap.name ) @@ -487,7 +473,7 @@ def test_api_put_update_post_boot(uvm_plain, io_engine): iface_id = "1" tapname = test_microvm.id[:8] + "tap" + iface_id - tap1 = net_tools.Tap(tapname, test_microvm.netns.id) + tap1 = net_tools.Tap(tapname, test_microvm.netns) test_microvm.api.network.put( iface_id=iface_id, host_dev_name=tap1.name, guest_mac="06:00:00:00:00:01" @@ -595,7 +581,7 @@ def test_rate_limiters_api_config(uvm_plain, io_engine): # Test network with tx bw rate-limiting. iface_id = "1" tapname = test_microvm.id[:8] + "tap" + iface_id - tap1 = net_tools.Tap(tapname, test_microvm.netns.id) + tap1 = net_tools.Tap(tapname, test_microvm.netns) test_microvm.api.network.put( iface_id=iface_id, @@ -607,7 +593,7 @@ def test_rate_limiters_api_config(uvm_plain, io_engine): # Test network with rx bw rate-limiting. iface_id = "2" tapname = test_microvm.id[:8] + "tap" + iface_id - tap2 = net_tools.Tap(tapname, test_microvm.netns.id) + tap2 = net_tools.Tap(tapname, test_microvm.netns) test_microvm.api.network.put( iface_id=iface_id, guest_mac="06:00:00:00:00:02", @@ -618,7 +604,7 @@ def test_rate_limiters_api_config(uvm_plain, io_engine): # Test network with tx and rx bw and ops rate-limiting. iface_id = "3" tapname = test_microvm.id[:8] + "tap" + iface_id - tap3 = net_tools.Tap(tapname, test_microvm.netns.id) + tap3 = net_tools.Tap(tapname, test_microvm.netns) test_microvm.api.network.put( iface_id=iface_id, guest_mac="06:00:00:00:00:03", @@ -665,7 +651,7 @@ def test_api_patch_pre_boot(uvm_plain, io_engine): iface_id = "1" tapname = test_microvm.id[:8] + "tap" + iface_id - tap1 = net_tools.Tap(tapname, test_microvm.netns.id) + tap1 = net_tools.Tap(tapname, test_microvm.netns) test_microvm.api.network.put( iface_id=iface_id, host_dev_name=tap1.name, guest_mac="06:00:00:00:00:01" ) @@ -714,7 +700,7 @@ def test_negative_api_patch_post_boot(uvm_plain, io_engine): iface_id = "1" tapname = test_microvm.id[:8] + "tap" + iface_id - tap1 = net_tools.Tap(tapname, test_microvm.netns.id) + tap1 = net_tools.Tap(tapname, test_microvm.netns) test_microvm.api.network.put( iface_id=iface_id, host_dev_name=tap1.name, guest_mac="06:00:00:00:00:01" ) @@ -734,7 +720,7 @@ def test_negative_api_patch_post_boot(uvm_plain, io_engine): test_microvm.api.logger.patch(level="Error") -def test_drive_patch(uvm_plain): +def test_drive_patch(uvm_plain, io_engine): """ Extensively test drive PATCH scenarios before and after boot. """ @@ -751,7 +737,7 @@ def test_drive_patch(uvm_plain): path_on_host=fs.path, is_root_device=False, is_read_only=False, - io_engine="Async" if is_io_uring_supported() else "Sync", + io_engine=io_engine, ) fs_vub = drive_tools.FilesystemFile( @@ -765,43 +751,33 @@ def test_drive_patch(uvm_plain): test_microvm.start() - _drive_patch(test_microvm) + _drive_patch(test_microvm, io_engine) @pytest.mark.skipif( platform.machine() != "x86_64", reason="not yet implemented on aarch64" ) -def test_send_ctrl_alt_del(uvm_plain): +def test_send_ctrl_alt_del(uvm_plain_any): """ Test shutting down the microVM gracefully on x86, by sending CTRL+ALT+DEL. """ # This relies on the i8042 device and AT Keyboard support being present in # the guest kernel. - test_microvm = uvm_plain + test_microvm = uvm_plain_any test_microvm.spawn() test_microvm.basic_config() + test_microvm.add_net_iface() test_microvm.start() - # Wait around for the guest to boot up and initialize the user space - time.sleep(2) - test_microvm.api.actions.put(action_type="SendCtrlAltDel") - firecracker_pid = test_microvm.firecracker_pid - # If everything goes as expected, the guest OS will issue a reboot, # causing Firecracker to exit. - # waitpid should block until the Firecracker process has exited. If - # it has already exited by the time we call waitpid, WNOHANG causes - # waitpid to raise a ChildProcessError exception. - try: - os.waitpid(firecracker_pid, os.WNOHANG) - except ChildProcessError: - pass + test_microvm.mark_killed() -def _drive_patch(test_microvm): +def _drive_patch(test_microvm, io_engine): """Exercise drive patch test scenarios.""" # Patches without mandatory fields for virtio block are not allowed. expected_msg = "Unable to patch the block device: Device manager error: Running method expected different backend. Please verify the request arguments" @@ -895,7 +871,7 @@ def _drive_patch(test_microvm): "is_root_device": True, "cache_type": "Unsafe", "is_read_only": True, - "path_on_host": "/ubuntu-22.04.squashfs", + "path_on_host": "/" + test_microvm.rootfs_file.name, "rate_limiter": None, "io_engine": "Sync", "socket": None, @@ -911,7 +887,7 @@ def _drive_patch(test_microvm): "bandwidth": {"size": 5000, "one_time_burst": None, "refill_time": 100}, "ops": {"size": 500, "one_time_burst": None, "refill_time": 100}, }, - "io_engine": "Async" if is_io_uring_supported() else "Sync", + "io_engine": io_engine, "socket": None, }, { @@ -1166,10 +1142,7 @@ def test_get_full_config_after_restoring_snapshot(microvm_factory, uvm_nano): ] snapshot = uvm_nano.snapshot_full() - uvm2 = microvm_factory.build() - uvm2.spawn() - uvm2.restore_from_snapshot(snapshot, resume=True) - + uvm2 = microvm_factory.build_from_snapshot(snapshot) expected_cfg = setup_cfg.copy() # We expect boot-source to be set with the following values @@ -1226,7 +1199,7 @@ def test_get_full_config(uvm_plain): "is_root_device": True, "cache_type": "Unsafe", "is_read_only": True, - "path_on_host": "/ubuntu-22.04.squashfs", + "path_on_host": "/" + test_microvm.rootfs_file.name, "rate_limiter": None, "io_engine": "Sync", "socket": None, @@ -1248,7 +1221,7 @@ def test_get_full_config(uvm_plain): # Add a net device. iface_id = "1" tapname = test_microvm.id[:8] + "tap" + iface_id - tap1 = net_tools.Tap(tapname, test_microvm.netns.id) + tap1 = net_tools.Tap(tapname, test_microvm.netns) guest_mac = "06:00:00:00:00:01" tx_rl = { "bandwidth": {"size": 1000000, "refill_time": 100, "one_time_burst": None}, diff --git a/tests/integration_tests/functional/test_balloon.py b/tests/integration_tests/functional/test_balloon.py index ee750dcac7d..c483683b7bb 100644 --- a/tests/integration_tests/functional/test_balloon.py +++ b/tests/integration_tests/functional/test_balloon.py @@ -7,6 +7,7 @@ from subprocess import TimeoutExpired import pytest +import requests from tenacity import retry, stop_after_attempt, wait_fixed from framework.utils import check_output, get_free_mem_ssh @@ -62,18 +63,10 @@ def lower_ssh_oom_chance(ssh_connection): def make_guest_dirty_memory(ssh_connection, amount_mib=32): """Tell the guest, over ssh, to dirty `amount` pages of memory.""" - logger = logging.getLogger("make_guest_dirty_memory") - lower_ssh_oom_chance(ssh_connection) - cmd = f"/usr/local/bin/fillmem {amount_mib}" try: - exit_code, stdout, stderr = ssh_connection.run(cmd, timeout=1.0) - # add something to the logs for troubleshooting - if exit_code != 0: - logger.error("while running: %s", cmd) - logger.error("stdout: %s", stdout) - logger.error("stderr: %s", stderr) + _ = ssh_connection.run(f"/usr/local/bin/fillmem {amount_mib}", timeout=1.0) except TimeoutExpired: # It's ok if this expires. Sometimes the SSH connection # gets killed by the OOM killer *after* the fillmem program @@ -215,12 +208,20 @@ def test_deflate_on_oom(uvm_plain_any, deflate_on_oom): balloon_size_before = test_microvm.api.balloon_stats.get().json()["actual_mib"] make_guest_dirty_memory(test_microvm.ssh, 128) - balloon_size_after = test_microvm.api.balloon_stats.get().json()["actual_mib"] - print(f"size before: {balloon_size_before} size after: {balloon_size_after}") - if deflate_on_oom: - assert balloon_size_after < balloon_size_before, "Balloon did not deflate" + try: + balloon_size_after = test_microvm.api.balloon_stats.get().json()["actual_mib"] + except requests.exceptions.ConnectionError: + assert ( + not deflate_on_oom + ), "Guest died even though it should have deflated balloon to alleviate memory pressure" + + test_microvm.mark_killed() else: - assert balloon_size_after >= balloon_size_before, "Balloon deflated" + print(f"size before: {balloon_size_before} size after: {balloon_size_after}") + if deflate_on_oom: + assert balloon_size_after < balloon_size_before, "Balloon did not deflate" + else: + assert balloon_size_after >= balloon_size_before, "Balloon deflated" # pylint: disable=C0103 @@ -484,9 +485,7 @@ def test_balloon_snapshot(microvm_factory, guest_kernel, rootfs): assert first_reading > second_reading snapshot = vm.snapshot_full() - microvm = microvm_factory.build() - microvm.spawn() - microvm.restore_from_snapshot(snapshot, resume=True) + microvm = microvm_factory.build_from_snapshot(snapshot) # Get the firecracker from snapshot pid, and open an ssh connection. firecracker_pid = microvm.firecracker_pid diff --git a/tests/integration_tests/functional/test_cmd_line_parameters.py b/tests/integration_tests/functional/test_cmd_line_parameters.py index 25e47a50e17..79af938b1f7 100644 --- a/tests/integration_tests/functional/test_cmd_line_parameters.py +++ b/tests/integration_tests/functional/test_cmd_line_parameters.py @@ -96,9 +96,7 @@ def test_cli_metrics_if_resume_no_metrics(uvm_plain, microvm_factory): snapshot = uvm1.snapshot_full() # When: restoring from the snapshot - uvm2 = microvm_factory.build() - uvm2.spawn() - uvm2.restore_from_snapshot(snapshot) + uvm2 = microvm_factory.build_from_snapshot(snapshot) # Then: the old metrics configuration does not exist metrics2 = Path(uvm2.jailer.chroot_path()) / metrics_path.name diff --git a/tests/integration_tests/functional/test_cpu_all.py b/tests/integration_tests/functional/test_cpu_all.py new file mode 100644 index 00000000000..6b934ffa394 --- /dev/null +++ b/tests/integration_tests/functional/test_cpu_all.py @@ -0,0 +1,43 @@ +# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Test all vCPUs are configured correctly and work properly. + +This test suite aims to catch bugs of Firecracker's vCPU configuration and +CPU templates especially under multi-vCPU setup, by checking that all vCPUs +are operating identically, except for the expected differences. +""" + +import pytest + +# Use the maximum number of vCPUs supported by Firecracker +MAX_VCPUS = 32 + + +@pytest.mark.parametrize("vcpu_count", [MAX_VCPUS]) +def test_all_vcpus_online(uvm_any): + """Check all vCPUs are online inside guest""" + assert ( + uvm_any.ssh.check_output("cat /sys/devices/system/cpu/online").stdout.strip() + == f"0-{uvm_any.vcpus_count - 1}" + ) + + +@pytest.mark.parametrize("vcpu_count", [MAX_VCPUS]) +def test_all_vcpus_have_same_features(uvm_any): + """ + Check all vCPUs have the same features inside guest. + + This test ensures Firecracker or CPU templates don't configure CPU features + differently between vCPUs. + + Note that whether the shown CPU features are expected or not should be + tested in (arch-specific) test_cpu_features_*.py only for vCPU 0. Thus, we + only test the equivalence of all CPUs in the same guest. + """ + # Get a feature set for each CPU and deduplicate them. + unique_feature_lists = uvm_any.ssh.check_output( + 'grep -E "^(flags|Features)" /proc/cpuinfo | uniq' + ).stdout.splitlines() + assert len(unique_feature_lists) == 1 diff --git a/tests/integration_tests/functional/test_cpu_features_aarch64.py b/tests/integration_tests/functional/test_cpu_features_aarch64.py index 3bfa2357d8b..ab59f79c706 100644 --- a/tests/integration_tests/functional/test_cpu_features_aarch64.py +++ b/tests/integration_tests/functional/test_cpu_features_aarch64.py @@ -2,179 +2,57 @@ # SPDX-License-Identifier: Apache-2.0 """Tests for the CPU features for aarch64.""" -import os -import platform -import re - import pytest -import framework.utils_cpuid as cpuid_utils -from framework import utils +from framework.properties import global_props from framework.utils_cpuid import CPU_FEATURES_CMD, CpuModel -PLATFORM = platform.machine() +pytestmark = pytest.mark.skipif( + global_props.cpu_architecture != "aarch64", reason="Only run in aarch64" +) -DEFAULT_G2_FEATURES = set( +G2_FEATS = set( ( "fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp " "asimdhp cpuid asimdrdm lrcpc dcpop asimddp ssbs" - ).split(" ") -) - -DEFAULT_G3_FEATURES_5_10 = DEFAULT_G2_FEATURES | set( - "sha512 asimdfhm dit uscat ilrcpc flagm jscvt fcma sha3 sm3 sm4 rng dcpodp i8mm bf16 dgh".split( - " " - ) + ).split() ) -DEFAULT_G3_FEATURES_WITH_SVE_AND_PAC_5_10 = DEFAULT_G3_FEATURES_5_10 | set( - "paca pacg sve svebf16 svei8mm".split(" ") +G3_FEATS = G2_FEATS | set( + "sha512 asimdfhm dit uscat ilrcpc flagm jscvt fcma sha3 sm3 sm4 rng dcpodp i8mm bf16 dgh".split() ) -DEFAULT_G3_FEATURES_V1N1 = DEFAULT_G2_FEATURES +G3_SVE_AND_PAC = set("paca pacg sve svebf16 svei8mm".split()) +G4_FEATS = (G3_FEATS | set("bti flagm2 frint sb".split())) - set("sm3 sm4".split()) -def _check_cpu_features_arm(test_microvm, guest_kv, template_name=None): - expected_cpu_features = {"Flags": []} - match cpuid_utils.get_cpu_model_name(), guest_kv, template_name: - case CpuModel.ARM_NEOVERSE_N1, _, "v1n1": - expected_cpu_features = DEFAULT_G2_FEATURES - case CpuModel.ARM_NEOVERSE_N1, _, None: - expected_cpu_features = DEFAULT_G2_FEATURES - - # [cm]7g with guest kernel 5.10 and later - case CpuModel.ARM_NEOVERSE_V1, _, "v1n1": - expected_cpu_features = DEFAULT_G3_FEATURES_V1N1 - case CpuModel.ARM_NEOVERSE_V1, _, "aarch64_with_sve_and_pac": - expected_cpu_features = DEFAULT_G3_FEATURES_WITH_SVE_AND_PAC_5_10 - case CpuModel.ARM_NEOVERSE_V1, _, None: - expected_cpu_features = DEFAULT_G3_FEATURES_5_10 - - _, stdout, _ = test_microvm.ssh.check_output(CPU_FEATURES_CMD) - flags = set(stdout.strip().split(" ")) - assert flags == expected_cpu_features - - -def get_cpu_template_dir(cpu_template): - """ - Utility function to return a valid string which will be used as - name of the directory where snapshot artifacts are stored during - snapshot test and loaded from during restore test. - - """ - return cpu_template if cpu_template else "none" - - -@pytest.mark.skipif( - PLATFORM != "aarch64", - reason="This is aarch64 specific test.", +G4_SVE_AND_PAC = set( + "paca pacg sve sve2 sveaes svebitperm svepmull svesha3 svebf16 svei8mm".split() ) -def test_host_vs_guest_cpu_features_aarch64(uvm_nano): - """Check CPU features host vs guest""" - vm = uvm_nano - vm.add_net_iface() - vm.start() - host_feats = set(utils.check_output(CPU_FEATURES_CMD).stdout.strip().split(" ")) - guest_feats = set(vm.ssh.check_output(CPU_FEATURES_CMD).stdout.strip().split(" ")) - cpu_model = cpuid_utils.get_cpu_model_name() - match cpu_model: - case CpuModel.ARM_NEOVERSE_N1: - assert host_feats - guest_feats == set() - # Kernel should hide this feature, but our guest kernel - # currently lacks the commit with this change. - # The commit that introduces the change: - # https://github.com/torvalds/linux/commit/7187bb7d0b5c7dfa18ca82e9e5c75e13861b1d88 - assert guest_feats - host_feats == {"ssbs"} - case CpuModel.ARM_NEOVERSE_V1: - # KVM does not enable PAC or SVE features by default - # and Firecracker does not enable them either. - assert host_feats - guest_feats == { - "paca", - "pacg", - "sve", - "svebf16", - "svei8mm", - } - # kernel should hide this feature, but our guest kernel - # is not recent enough for this. - assert guest_feats - host_feats == {"ssbs"} - case _: - if os.environ.get("BUILDKITE") is not None: - assert False, f"Cpu model {cpu_model} is not supported" +def test_guest_cpu_features(uvm_any): + """Check the CPU features for a microvm with different CPU templates""" + vm = uvm_any + expected_cpu_features = set() + match global_props.cpu_model, vm.cpu_template_name: + case CpuModel.ARM_NEOVERSE_N1, "v1n1": + expected_cpu_features = G2_FEATS + case CpuModel.ARM_NEOVERSE_N1, None: + expected_cpu_features = G2_FEATS -@pytest.mark.skipif( - PLATFORM != "aarch64", - reason="This is aarch64 specific test.", -) -def test_default_cpu_features(microvm_factory, guest_kernel, rootfs_ubuntu_22): - """ - Check the CPU features for a microvm with the specified config. - """ - - vm = microvm_factory.build(guest_kernel, rootfs_ubuntu_22, monitor_memory=False) - vm.spawn() - vm.basic_config() - vm.add_net_iface() - vm.start() - guest_kv = re.search(r"vmlinux-(\d+\.\d+)", guest_kernel.name).group(1) - _check_cpu_features_arm(vm, guest_kv) - - -@pytest.mark.skipif( - PLATFORM != "aarch64", - reason="This is aarch64 specific test.", -) -def test_cpu_features_with_static_template( - microvm_factory, guest_kernel, rootfs_ubuntu_22, cpu_template -): - """ - Check the CPU features for a microvm with the specified config. - """ - - vm = microvm_factory.build(guest_kernel, rootfs_ubuntu_22, monitor_memory=False) - vm.spawn() - vm.basic_config(cpu_template=cpu_template) - vm.add_net_iface() - vm.start() - guest_kv = re.search(r"vmlinux-(\d+\.\d+)", guest_kernel.name).group(1) - _check_cpu_features_arm(vm, guest_kv, "v1n1") - - # Check that cpu features are still correct - # after snap/restore cycle. - snapshot = vm.snapshot_full() - restored_vm = microvm_factory.build() - restored_vm.spawn() - restored_vm.restore_from_snapshot(snapshot, resume=True) - _check_cpu_features_arm(restored_vm, guest_kv, "v1n1") - - -@pytest.mark.skipif( - PLATFORM != "aarch64", - reason="This is aarch64 specific test.", -) -def test_cpu_features_with_custom_template( - microvm_factory, guest_kernel, rootfs_ubuntu_22, custom_cpu_template -): - """ - Check the CPU features for a microvm with the specified config. - """ - - vm = microvm_factory.build(guest_kernel, rootfs_ubuntu_22, monitor_memory=False) - vm.spawn() - vm.basic_config() - vm.api.cpu_config.put(**custom_cpu_template["template"]) - vm.add_net_iface() - vm.start() - guest_kv = re.search(r"vmlinux-(\d+\.\d+)", guest_kernel.name).group(1) - _check_cpu_features_arm(vm, guest_kv, custom_cpu_template["name"]) - - # Check that cpu features are still correct - # after snap/restore cycle. - snapshot = vm.snapshot_full() - restored_vm = microvm_factory.build() - restored_vm.spawn() - restored_vm.restore_from_snapshot(snapshot, resume=True) - _check_cpu_features_arm(restored_vm, guest_kv, custom_cpu_template["name"]) + # [cm]7g with guest kernel 5.10 and later + case CpuModel.ARM_NEOVERSE_V1, "v1n1": + expected_cpu_features = G2_FEATS + case CpuModel.ARM_NEOVERSE_V1, "aarch64_with_sve_and_pac": + expected_cpu_features = G3_FEATS | G3_SVE_AND_PAC + case CpuModel.ARM_NEOVERSE_V1, None: + expected_cpu_features = G3_FEATS + case CpuModel.ARM_NEOVERSE_V2, None: + expected_cpu_features = G4_FEATS + case CpuModel.ARM_NEOVERSE_V2, "aarch64_with_sve_and_pac": + expected_cpu_features = G4_FEATS | G4_SVE_AND_PAC + + guest_feats = set(vm.ssh.check_output(CPU_FEATURES_CMD).stdout.split()) + assert guest_feats == expected_cpu_features diff --git a/tests/integration_tests/functional/test_cpu_features_host_vs_guest.py b/tests/integration_tests/functional/test_cpu_features_host_vs_guest.py new file mode 100644 index 00000000000..4b66b077839 --- /dev/null +++ b/tests/integration_tests/functional/test_cpu_features_host_vs_guest.py @@ -0,0 +1,403 @@ +# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=too-many-statements +# pylint: disable=too-many-branches + +""" +Check CPU features in the host vs the guest. + +This test can highlight differences between the host and what the guest sees. + +No CPU templates as we are interested only on what is passed through to the guest by default. +For that, check test_feat_parity.py +""" + +import os + +from framework import utils +from framework.properties import global_props +from framework.utils_cpuid import CPU_FEATURES_CMD, CpuModel + +CPU_MODEL = global_props.cpu_codename + +INTEL_HOST_ONLY_FEATS = { + "acpi", + "aperfmperf", + "arch_perfmon", + "art", + "bts", + "cat_l3", + "cdp_l3", + "cqm", + "cqm_llc", + "cqm_mbm_local", + "cqm_mbm_total", + "cqm_occup_llc", + "dca", + "ds_cpl", + "dtes64", + "dtherm", + "dts", + "epb", + "ept", + "ept_ad", + "est", + "flexpriority", + "flush_l1d", + "hwp", + "hwp_act_window", + "hwp_epp", + "hwp_pkg_req", + "ida", + "intel_ppin", + "intel_pt", + "mba", + "monitor", + "pbe", + "pdcm", + "pebs", + "pln", + "pts", + "rdt_a", + "sdbg", + "smx", + "tm", + "tm2", + "tpr_shadow", + "vmx", + "vnmi", + "vpid", + "xtpr", +} + +INTEL_GUEST_ONLY_FEATS = { + "hypervisor", + "tsc_known_freq", + "umip", +} + +AMD_MILAN_HOST_ONLY_FEATS = { + "amd_ppin", + "aperfmperf", + "bpext", + "cat_l3", + "cdp_l3", + "cpb", + "cqm", + "cqm_llc", + "cqm_mbm_local", + "cqm_mbm_total", + "cqm_occup_llc", + "decodeassists", + "extapic", + "extd_apicid", + "flushbyasid", + "hw_pstate", + "ibs", + "irperf", + "lbrv", + "mba", + "monitor", + "mwaitx", + "overflow_recov", + "pausefilter", + "perfctr_llc", + "perfctr_nb", + "pfthreshold", + "rdpru", + "rdt_a", + "sev", + "sev_es", + "skinit", + "smca", + "sme", + "succor", + "svm_lock", + "tce", + "tsc_scale", + "v_vmsave_vmload", + "vgif", + "vmcb_clean", + "wdt", +} + +AMD_GUEST_ONLY_FEATS = { + "hypervisor", + "tsc_adjust", + "tsc_deadline_timer", + "tsc_known_freq", +} + +AMD_MILAN_HOST_ONLY_FEATS_6_1 = AMD_MILAN_HOST_ONLY_FEATS - { + "lbrv", + "pausefilter", + "pfthreshold", + "sme", + "tsc_scale", + "v_vmsave_vmload", + "vgif", + "vmcb_clean", +} | {"brs", "rapl", "v_spec_ctrl"} + +AMD_GENOA_HOST_ONLY_FEATS = AMD_MILAN_HOST_ONLY_FEATS | { + "avic", + "flush_l1d", + "ibrs_enhanced", +} + +AMD_GENOA_HOST_ONLY_FEATS_6_1 = AMD_MILAN_HOST_ONLY_FEATS_6_1 - {"brs"} | { + "avic", + "amd_lbr_v2", + "cppc", + "flush_l1d", + "ibrs_enhanced", + "perfmon_v2", + "x2avic", +} + + +def test_host_vs_guest_cpu_features(uvm_plain_any): + """Check CPU features host vs guest""" + + vm = uvm_plain_any + vm.spawn() + vm.basic_config() + vm.add_net_iface() + vm.start() + host_feats = set(utils.check_output(CPU_FEATURES_CMD).stdout.split()) + guest_feats = set(vm.ssh.check_output(CPU_FEATURES_CMD).stdout.split()) + + match CPU_MODEL: + case CpuModel.AMD_MILAN: + if global_props.host_linux_version_tpl < (6, 1): + assert host_feats - guest_feats == AMD_MILAN_HOST_ONLY_FEATS + else: + assert host_feats - guest_feats == AMD_MILAN_HOST_ONLY_FEATS_6_1 + + assert guest_feats - host_feats == AMD_GUEST_ONLY_FEATS + + case CpuModel.AMD_GENOA: + if global_props.host_linux_version_tpl < (6, 1): + assert host_feats - guest_feats == AMD_GENOA_HOST_ONLY_FEATS + else: + assert host_feats - guest_feats == AMD_GENOA_HOST_ONLY_FEATS_6_1 + + assert guest_feats - host_feats == AMD_GUEST_ONLY_FEATS + + case CpuModel.INTEL_SKYLAKE: + assert host_feats - guest_feats == INTEL_HOST_ONLY_FEATS + assert guest_feats - host_feats == INTEL_GUEST_ONLY_FEATS + + case CpuModel.INTEL_CASCADELAKE: + expected_host_minus_guest = INTEL_HOST_ONLY_FEATS + expected_guest_minus_host = INTEL_GUEST_ONLY_FEATS + + # Linux kernel v6.4+ passes through the CPUID bit for "flush_l1d" to guests. + # https://github.com/torvalds/linux/commit/45cf86f26148e549c5ba4a8ab32a390e4bde216e + # + # Our test ubuntu host kernel is v6.8 and has the commit. + if global_props.host_linux_version_tpl >= (6, 4): + expected_host_minus_guest -= {"flush_l1d"} + + # Linux kernel v6.6+ drops the "invpcid_single" synthetic feature bit. + # https://github.com/torvalds/linux/commit/54e3d9434ef61b97fd3263c141b928dc5635e50d + # + # Our test ubuntu host kernel is v6.8 and has the commit. + host_has_invpcid_single = global_props.host_linux_version_tpl < (6, 6) + guest_has_invpcid_single = vm.guest_kernel_version < (6, 6) + if host_has_invpcid_single and not guest_has_invpcid_single: + expected_host_minus_guest |= {"invpcid_single"} + if not host_has_invpcid_single and guest_has_invpcid_single: + expected_guest_minus_host |= {"invpcid_single"} + + assert host_feats - guest_feats == expected_host_minus_guest + assert guest_feats - host_feats == expected_guest_minus_host + + case CpuModel.INTEL_ICELAKE: + host_guest_diff_5_10 = INTEL_HOST_ONLY_FEATS - {"cdp_l3"} | { + "pconfig", + "tme", + "split_lock_detect", + } + host_guest_diff_6_1 = host_guest_diff_5_10 - { + "bts", + "dtes64", + "dts", + "pebs", + } + + if global_props.host_linux_version_tpl < (6, 1): + assert host_feats - guest_feats == host_guest_diff_5_10 + else: + assert host_feats - guest_feats == host_guest_diff_6_1 + assert guest_feats - host_feats == INTEL_GUEST_ONLY_FEATS - {"umip"} + + case CpuModel.INTEL_SAPPHIRE_RAPIDS: + expected_host_minus_guest = INTEL_HOST_ONLY_FEATS.copy() + expected_guest_minus_host = INTEL_GUEST_ONLY_FEATS.copy() + + host_version = global_props.host_linux_version_tpl + guest_version = vm.guest_kernel_version + + # KVM does not support virtualization of the following hardware features yet for several + # reasons (e.g. security, simply difficulty of implementation). + expected_host_minus_guest |= { + # Intel Total Memory Encryption (TME) is the capability to encrypt the entirety of + # physical memory of a system. TME is enabled by system BIOS/hardware and applies to + # the phyiscal memory as a whole. + "tme", + # PCONFIG instruction allows software to configure certain platform features. It + # supports these features with multiple leaf functions, selecting a leaf function + # using the value in EAX. As of this writing, the only defined PCONFIG leaf function + # is for key programming for total memory encryption-multi-key (TME-MK). + "pconfig", + # Architectural Last Branch Record (Arch LBR) that is a feature that logs the most + # recently executed branch instructions (e.g. source and destination addresses). + # Traditional LBR implementations have existed in Intel CPUs for years and the MSR + # interface varied by CPU model. Arch LBR is a standardized version. There is a + # kernel patch created in 2022 but didn't get merged due to a mess. + # https://lore.kernel.org/all/20221125040604.5051-1-weijiang.yang@intel.com/ + "arch_lbr", + # ENQCMD/ENQCMDS are instructions that allow software to atomically write 64-byte + # commands to enqueue registers, which are special device registers accessed using + # memory-mapped I/O. + "enqcmd", + # Intel Resource Director Technology (RDT) feature set provides a set of allocation + # (resource control) capabilities including Cache Allocation Technology (CAT) and + # Code and Data Prioritization (CDP). + # L3 variants are listed in INTEL_HOST_ONLY_FEATS. + "cat_l2", + "cdp_l2", + # This is a synthesized bit for split lock detection that raise an Alignment Check + # (#AC) exception if an operand of an atomic operation crosses two cache lines. It + # is not enumerated on CPUID, instead detected by actually attempting to read from + # MSR address 0x33 (MSR_MEMORY_CTRL in Intel SDM, MSR_TEST_CTRL in Linux kernel). + "split_lock_detect", + # Firecracker disables WAITPKG in CPUID normalization. + # https://github.com/firecracker-microvm/firecracker/pull/5118 + "waitpkg", + } + + # The following features are also not virtualized by KVM yet but are only supported on + # newer kernel versions. + if host_version >= (5, 18): + expected_host_minus_guest |= { + # Hardware Feedback Interface (HFI) is a feature that gives OSes a performance + # and energy efficiency capability data for each CPU that can be used to + # influence task placement decisions. + # https://github.com/torvalds/linux/commit/7b8f40b3de75c971a4e5f9308b06deb59118dbac + "hfi", + # Indirect Brach Tracking (IBT) is a feature where the CPU ensures that indirect + # branch targets start with ENDBRANCH instruction (`endbr32` or `endbr64`), + # which executes as a no-op; if anything else is found, a control-protection + # (#CP) fault will be raised. + # https://github.com/torvalds/linux/commit/991625f3dd2cbc4b787deb0213e2bcf8fa264b21 + "ibt", + } + + # AVX512 FP16 is supported and passed through on v5.11+. + # https://github.com/torvalds/linux/commit/e1b35da5e624f8b09d2e98845c2e4c84b179d9a4 + # https://github.com/torvalds/linux/commit/2224fc9efb2d6593fbfb57287e39ba4958b188ba + if host_version >= (5, 11) and guest_version < (5, 11): + expected_host_minus_guest |= {"avx512_fp16"} + + # AVX VNNI support is supported and passed through on v5.12+. + # https://github.com/torvalds/linux/commit/b85a0425d8056f3bd8d0a94ecdddf2a39d32a801 + # https://github.com/torvalds/linux/commit/1085a6b585d7d1c441cd10fdb4c7a4d96a22eba7 + if host_version >= (5, 12) and guest_version < (5, 12): + expected_host_minus_guest |= {"avx_vnni"} + + # Bus lock detection is supported on v5.12+ and passed through on v5.13+. + # https://github.com/torvalds/linux/commit/f21d4d3b97a8603567e5d4250bd75e8ebbd520af + # https://github.com/torvalds/linux/commit/76ea438b4afcd9ee8da3387e9af4625eaccff58f + if host_version >= (5, 13) and guest_version < (5, 12): + expected_host_minus_guest |= {"bus_lock_detect"} + + # Intel AMX is supported and passed through on v5.17+. + # https://github.com/torvalds/linux/commit/690a757d610e50c2c3acd2e4bc3992cfc63feff2 + if host_version >= (5, 17) and guest_version < (5, 17): + expected_host_minus_guest |= {"amx_bf16", "amx_int8", "amx_tile"} + + expected_guest_minus_host -= { + # UMIP can be emulated by KVM on Intel processors, but is supported in hardware on + # Intel Sapphire Rapids and passed through. + "umip", + # This is a synthesized bit and it is always set on guest thanks to kvm-clock. But + # Intel Sapphire Rapids reports TSC frequency on CPUID leaf 0x15, so the bit is also + # set on host. + "tsc_known_freq", + } + + assert host_feats - guest_feats == expected_host_minus_guest + assert guest_feats - host_feats == expected_guest_minus_host + + case CpuModel.ARM_NEOVERSE_N1: + expected_guest_minus_host = set() + expected_host_minus_guest = set() + + # Upstream kernel v6.11+ hides "ssbs" from "lscpu" on Neoverse-N1 and Neoverse-V1 since + # they have an errata whereby an MSR to the SSBS special-purpose register does not + # affect subsequent speculative instructions, permitting speculative store bypassing for + # a window of time. + # https://github.com/torvalds/linux/commit/adeec61a4723fd3e39da68db4cc4d924e6d7f641 + # + # While Amazon Linux kernels (v5.10 and v6.1) backported the above commit, our test + # ubuntu kernel (v6.8) and our guest kernels (v5.10 and v6.1) don't pick it. + host_has_ssbs = global_props.host_os not in { + "amzn2", + "amzn2023", + } and global_props.host_linux_version_tpl < (6, 11) + guest_has_ssbs = vm.guest_kernel_version < (6, 11) + + if host_has_ssbs and not guest_has_ssbs: + expected_host_minus_guest |= {"ssbs"} + if not host_has_ssbs and guest_has_ssbs: + expected_guest_minus_host |= {"ssbs"} + + assert host_feats - guest_feats == expected_host_minus_guest + assert guest_feats - host_feats == expected_guest_minus_host + + case CpuModel.ARM_NEOVERSE_V1 | CpuModel.ARM_NEOVERSE_V2: + expected_guest_minus_host = set() + # KVM does not enable PAC or SVE features by default + # and Firecracker does not enable them either. + expected_host_minus_guest = {"paca", "pacg", "sve", "svebf16", "svei8mm"} + + if CPU_MODEL == CpuModel.ARM_NEOVERSE_V2: + expected_host_minus_guest |= { + "svebitperm", + "svesha3", + "sveaes", + "sve2", + "svepmull", + } + + # Upstream kernel v6.11+ hides "ssbs" from "lscpu" on Neoverse-N1 and Neoverse-V1 since + # they have an errata whereby an MSR to the SSBS special-purpose register does not + # affect subsequent speculative instructions, permitting speculative store bypassing for + # a window of time. + # https://github.com/torvalds/linux/commit/adeec61a4723fd3e39da68db4cc4d924e6d7f641 + # + # While Amazon Linux kernels (v5.10 and v6.1) backported the above commit, our test + # ubuntu kernel (v6.8) and our guest kernels (v5.10 and v6.1) don't pick it. + host_has_ssbs = global_props.host_os not in { + "amzn2", + "amzn2023", + } and global_props.host_linux_version_tpl < (6, 11) + guest_has_ssbs = vm.guest_kernel_version < (6, 11) + + if host_has_ssbs and not guest_has_ssbs: + expected_host_minus_guest |= {"ssbs"} + if not host_has_ssbs and guest_has_ssbs: + expected_guest_minus_host |= {"ssbs"} + + assert host_feats - guest_feats == expected_host_minus_guest + assert guest_feats - host_feats == expected_guest_minus_host + + case _: + # only fail if running in CI + if os.environ.get("BUILDKITE") is not None: + assert ( + guest_feats == host_feats + ), f"Cpu model {CPU_MODEL} is not supported" diff --git a/tests/integration_tests/functional/test_cpu_features_x86_64.py b/tests/integration_tests/functional/test_cpu_features_x86_64.py index 3cea004a6b4..0fb51e5bd37 100644 --- a/tests/integration_tests/functional/test_cpu_features_x86_64.py +++ b/tests/integration_tests/functional/test_cpu_features_x86_64.py @@ -22,7 +22,6 @@ from framework.defs import SUPPORTED_HOST_KERNELS from framework.properties import global_props from framework.utils_cpu_templates import SUPPORTED_CPU_TEMPLATES -from framework.utils_cpuid import CPU_FEATURES_CMD, CpuModel PLATFORM = platform.machine() UNSUPPORTED_HOST_KERNEL = ( @@ -30,6 +29,10 @@ ) DATA_FILES = Path("./data/msr") +pytestmark = pytest.mark.skipif( + global_props.cpu_architecture != "x86_64", reason="Only run in x86_64" +) + def read_msr_csv(fd): """Read a CSV of MSRs""" @@ -105,7 +108,6 @@ def skip_test_based_on_artifacts(snapshot_artifacts_dir): pytest.skip(re.sub(" +", " ", reason)) -@pytest.mark.skipif(PLATFORM != "x86_64", reason="CPUID is only supported on x86_64.") @pytest.mark.parametrize( "num_vcpus", [1, 2, 16], @@ -126,7 +128,6 @@ def test_cpuid(uvm_plain_any, num_vcpus, htt): _check_cpuid_x86(vm, num_vcpus, "true" if num_vcpus > 1 else "false") -@pytest.mark.skipif(PLATFORM != "x86_64", reason="CPUID is only supported on x86_64.") @pytest.mark.skipif( cpuid_utils.get_cpu_vendor() != cpuid_utils.CpuVendor.AMD, reason="L3 cache info is only present in 0x80000006 for AMD", @@ -143,15 +144,14 @@ def test_extended_cache_features(uvm_plain_any): _check_extended_cache_features(vm) -@pytest.mark.skipif( - PLATFORM != "x86_64", reason="The CPU brand string is masked only on x86_64." -) def test_brand_string(uvm_plain_any): """ Ensure good formatting for the guest brand string. * For Intel CPUs, the guest brand string should be: Intel(R) Xeon(R) Processor @ {host frequency} + or + Intel(R) Xeon(R) Processor where {host frequency} is the frequency reported by the host CPUID (e.g. 4.01GHz) * For AMD CPUs, the guest brand string should be: @@ -186,7 +186,9 @@ def test_brand_string(uvm_plain_any): cif = open("/proc/cpuinfo", "r", encoding="utf-8") cpu_info = cif.read() mo = re.search("model name.*:.* ([0-9]*.[0-9]*[G|M|T]Hz)", cpu_info) - assert mo + # Skip if host frequency is not reported + if mo is None: + return host_frequency = mo.group(1) # Assert the model name matches "Intel(R) Xeon(R) Processor @ " @@ -204,272 +206,6 @@ def test_brand_string(uvm_plain_any): assert False -@pytest.mark.skipif( - PLATFORM != "x86_64", - reason="This is x86_64 specific test.", -) -def test_host_vs_guest_cpu_features_x86_64(uvm_nano): - """Check CPU features host vs guest""" - - vm = uvm_nano - vm.add_net_iface() - vm.start() - host_feats = set(utils.check_output(CPU_FEATURES_CMD).stdout.strip().split(" ")) - guest_feats = set(vm.ssh.check_output(CPU_FEATURES_CMD).stdout.strip().split(" ")) - - cpu_model = cpuid_utils.get_cpu_codename() - match cpu_model: - case CpuModel.AMD_MILAN: - host_guest_diff_5_10 = { - "amd_ppin", - "aperfmperf", - "bpext", - "cat_l3", - "cdp_l3", - "cpb", - "cqm", - "cqm_llc", - "cqm_mbm_local", - "cqm_mbm_total", - "cqm_occup_llc", - "decodeassists", - "extapic", - "extd_apicid", - "flushbyasid", - "hw_pstate", - "ibs", - "irperf", - "lbrv", - "mba", - "monitor", - "mwaitx", - "overflow_recov", - "pausefilter", - "perfctr_llc", - "perfctr_nb", - "pfthreshold", - "rdpru", - "rdt_a", - "sev", - "sev_es", - "skinit", - "smca", - "sme", - "succor", - "svm_lock", - "tce", - "tsc_scale", - "v_vmsave_vmload", - "vgif", - "vmcb_clean", - "wdt", - } - - host_guest_diff_6_1 = host_guest_diff_5_10 - { - "lbrv", - "pausefilter", - "pfthreshold", - "sme", - "tsc_scale", - "v_vmsave_vmload", - "vgif", - "vmcb_clean", - } | {"brs", "rapl", "v_spec_ctrl"} - - if global_props.host_linux_version_tpl < (6, 1): - assert host_feats - guest_feats == host_guest_diff_5_10 - else: - assert host_feats - guest_feats == host_guest_diff_6_1 - - assert guest_feats - host_feats == { - "hypervisor", - "tsc_adjust", - "tsc_deadline_timer", - "tsc_known_freq", - } - case CpuModel.INTEL_SKYLAKE: - assert host_feats - guest_feats == { - "acpi", - "aperfmperf", - "arch_perfmon", - "art", - "bts", - "cat_l3", - "cdp_l3", - "cqm", - "cqm_llc", - "cqm_mbm_local", - "cqm_mbm_total", - "cqm_occup_llc", - "dca", - "ds_cpl", - "dtes64", - "dtherm", - "dts", - "epb", - "ept", - "ept_ad", - "est", - "flexpriority", - "flush_l1d", - "hwp", - "hwp_act_window", - "hwp_epp", - "hwp_pkg_req", - "ida", - "intel_ppin", - "intel_pt", - "mba", - "monitor", - "pbe", - "pdcm", - "pebs", - "pln", - "pts", - "rdt_a", - "sdbg", - "smx", - "tm", - "tm2", - "tpr_shadow", - "vmx", - "vnmi", - "vpid", - "xtpr", - } - assert guest_feats - host_feats == { - "hypervisor", - "tsc_known_freq", - "umip", - } - case CpuModel.INTEL_CASCADELAKE: - assert host_feats - guest_feats == { - "acpi", - "aperfmperf", - "arch_perfmon", - "art", - "bts", - "cat_l3", - "cdp_l3", - "cqm", - "cqm_llc", - "cqm_mbm_local", - "cqm_mbm_total", - "cqm_occup_llc", - "dca", - "ds_cpl", - "dtes64", - "dtherm", - "dts", - "epb", - "ept", - "ept_ad", - "est", - "flexpriority", - "flush_l1d", - "hwp", - "hwp_act_window", - "hwp_epp", - "hwp_pkg_req", - "ida", - "intel_ppin", - "intel_pt", - "mba", - "monitor", - "pbe", - "pdcm", - "pebs", - "pln", - "pts", - "rdt_a", - "sdbg", - "smx", - "tm", - "tm2", - "tpr_shadow", - "vmx", - "vnmi", - "vpid", - "xtpr", - } - assert guest_feats - host_feats == { - "hypervisor", - "tsc_known_freq", - "umip", - } - case CpuModel.INTEL_ICELAKE: - host_guest_diff_5_10 = { - "dtes64", - "hwp_act_window", - "pdcm", - "acpi", - "aperfmperf", - "arch_perfmon", - "art", - "bts", - "cat_l3", - "cqm", - "cqm_llc", - "cqm_mbm_local", - "cqm_mbm_total", - "cqm_occup_llc", - "dca", - "ds_cpl", - "dtherm", - "dts", - "epb", - "ept", - "ept_ad", - "est", - "flexpriority", - "flush_l1d", - "hwp", - "hwp_epp", - "hwp_pkg_req", - "ida", - "intel_ppin", - "intel_pt", - "mba", - "monitor", - "pbe", - "pconfig", - "pebs", - "pln", - "pts", - "rdt_a", - "sdbg", - "smx", - "split_lock_detect", - "tm", - "tm2", - "tme", - "tpr_shadow", - "vmx", - "vnmi", - "vpid", - "xtpr", - } - host_guest_diff_6_1 = host_guest_diff_5_10 - { - "bts", - "dtes64", - "dts", - "pebs", - } - - if global_props.host_linux_version_tpl < (6, 1): - assert host_feats - guest_feats == host_guest_diff_5_10 - else: - assert host_feats - guest_feats == host_guest_diff_6_1 - - assert guest_feats - host_feats == { - "hypervisor", - "tsc_known_freq", - } - case _: - if os.environ.get("BUILDKITE") is not None: - assert False, f"Cpu model {cpu_model} is not supported" - - # From the `Intel® 64 Architecture x2APIC Specification` # (https://courses.cs.washington.edu/courses/cse451/24wi/documentation/x2apic.pdf): # > The X2APIC MSRs cannot to be loaded and stored on VMX transitions. A VMX transition fails @@ -539,7 +275,7 @@ def msr_cpu_template_fxt(request): @pytest.mark.timeout(900) @pytest.mark.nonci def test_cpu_rdmsr( - microvm_factory, msr_cpu_template, guest_kernel, rootfs_ubuntu_22, results_dir + microvm_factory, msr_cpu_template, guest_kernel, rootfs, results_dir ): """ Test MSRs that are available to the guest. @@ -574,7 +310,7 @@ def test_cpu_rdmsr( """ vcpus, guest_mem_mib = 1, 1024 - vm = microvm_factory.build(guest_kernel, rootfs_ubuntu_22, monitor_memory=False) + vm = microvm_factory.build(guest_kernel, rootfs, monitor_memory=False) vm.spawn() vm.add_net_iface() vm.basic_config( @@ -582,7 +318,7 @@ def test_cpu_rdmsr( ) vm.start() vm.ssh.scp_put(DATA_FILES / "msr_reader.sh", "/tmp/msr_reader.sh") - _, stdout, stderr = vm.ssh.run("/tmp/msr_reader.sh") + _, stdout, stderr = vm.ssh.run("/tmp/msr_reader.sh", timeout=None) assert stderr == "" # Load results read from the microvm @@ -630,7 +366,9 @@ def dump_msr_state_to_file(dump_fname, ssh_conn, shared_names): ssh_conn.scp_put( shared_names["msr_reader_host_fname"], shared_names["msr_reader_guest_fname"] ) - _, stdout, stderr = ssh_conn.run(shared_names["msr_reader_guest_fname"]) + _, stdout, stderr = ssh_conn.run( + shared_names["msr_reader_guest_fname"], timeout=None + ) assert stderr == "" with open(dump_fname, "w", encoding="UTF-8") as file: @@ -643,9 +381,7 @@ def dump_msr_state_to_file(dump_fname, ssh_conn, shared_names): ) @pytest.mark.timeout(900) @pytest.mark.nonci -def test_cpu_wrmsr_snapshot( - microvm_factory, guest_kernel, rootfs_ubuntu_22, msr_cpu_template -): +def test_cpu_wrmsr_snapshot(microvm_factory, guest_kernel, rootfs, msr_cpu_template): """ This is the first part of the test verifying that MSRs retain their values after restoring from a snapshot. @@ -665,7 +401,7 @@ def test_cpu_wrmsr_snapshot( shared_names = SNAPSHOT_RESTORE_SHARED_NAMES vcpus, guest_mem_mib = 1, 1024 - vm = microvm_factory.build(guest_kernel, rootfs_ubuntu_22, monitor_memory=False) + vm = microvm_factory.build(guest_kernel, rootfs, monitor_memory=False) vm.spawn() vm.add_net_iface() vm.basic_config( @@ -686,7 +422,9 @@ def test_cpu_wrmsr_snapshot( wrmsr_input_guest_fname = "/tmp/wrmsr_input.txt" vm.ssh.scp_put(wrmsr_input_host_fname, wrmsr_input_guest_fname) - _, _, stderr = vm.ssh.run(f"{msr_writer_guest_fname} {wrmsr_input_guest_fname}") + _, _, stderr = vm.ssh.run( + f"{msr_writer_guest_fname} {wrmsr_input_guest_fname}", timeout=None + ) assert stderr == "" # Dump MSR state to a file that will be published to S3 for the 2nd part of the test @@ -800,9 +538,7 @@ def dump_cpuid_to_file(dump_fname, ssh_conn): ) @pytest.mark.timeout(900) @pytest.mark.nonci -def test_cpu_cpuid_snapshot( - microvm_factory, guest_kernel, rootfs_ubuntu_22, msr_cpu_template -): +def test_cpu_cpuid_snapshot(microvm_factory, guest_kernel, rootfs, msr_cpu_template): """ This is the first part of the test verifying that CPUID remains the same after restoring from a snapshot. @@ -818,7 +554,7 @@ def test_cpu_cpuid_snapshot( vm = microvm_factory.build( kernel=guest_kernel, - rootfs=rootfs_ubuntu_22, + rootfs=rootfs, ) vm.spawn() vm.add_net_iface() @@ -907,9 +643,6 @@ def test_cpu_cpuid_restore(microvm_factory, guest_kernel, msr_cpu_template): ) -@pytest.mark.skipif( - PLATFORM != "x86_64", reason="CPU features are masked only on x86_64." -) @pytest.mark.parametrize("cpu_template", ["T2", "T2S", "C3"]) def test_cpu_template(uvm_plain_any, cpu_template, microvm_factory): """ @@ -1194,9 +927,9 @@ def check_enabled_features(test_microvm, cpu_template): "enhanced REP MOVSB/STOSB": "true", "SMAP: supervisor mode access prevention": "true", # xsave_0xd_0 - "XCR0 supported: x87 state": "true", - "XCR0 supported: SSE state": "true", - "XCR0 supported: AVX state": "true", + "x87 state": "true", + "SSE state": "true", + "AVX state": "true", # xsave_0xd_1 "XSAVEOPT instruction": "true", # extended_080000001_edx @@ -1227,19 +960,16 @@ def check_enabled_features(test_microvm, cpu_template): ) -@pytest.mark.skipif(PLATFORM != "x86_64", reason="This test is specific to x86_64.") -def test_c3_on_skylake_show_warning(uvm_plain, cpu_template): +def test_c3_on_skylake_show_warning(uvm_plain, cpu_template_any): """ This test verifies that the warning message about MMIO stale data mitigation - is displayed only on Intel Skylake with C3 template. + is displayed only on Intel Skylake with static C3 template. """ uvm = uvm_plain uvm.spawn() - uvm.basic_config( - vcpu_count=2, - mem_size_mib=256, - cpu_template=cpu_template, - ) + uvm.basic_config(vcpu_count=2, mem_size_mib=256) + uvm.add_net_iface() + uvm.set_cpu_template(cpu_template_any) uvm.start() message = ( @@ -1248,7 +978,42 @@ def test_c3_on_skylake_show_warning(uvm_plain, cpu_template): "does not apply the mitigation against MMIO stale data " "vulnerability." ) - if cpu_template == "C3" and global_props.cpu_codename == "INTEL_SKYLAKE": + + if cpu_template_any == "C3" and global_props.cpu_codename == "INTEL_SKYLAKE": assert message in uvm.log_data else: assert message not in uvm.log_data + + +@pytest.mark.skipif( + global_props.cpu_codename != "INTEL_SAPPHIRE_RAPIDS" + or global_props.host_linux_version_tpl < (5, 17), + reason="Intel AMX is only supported on Intel Sapphire Rapids and kernel v5.17+", +) +def test_intel_amx_reported_on_sapphire_rapids( + microvm_factory, guest_kernel_linux_6_1, rootfs +): + """ + Verifies that Intel AMX is reported on guest (v5.17+) + """ + uvm = microvm_factory.build(guest_kernel_linux_6_1, rootfs) + uvm.spawn() + uvm.basic_config() + uvm.add_net_iface() + uvm.start() + + expected_dict = { + "AMX-BF16: tile bfloat16 support": "true", # CPUID.(EAX=07H,ECX=0):EDX[22] + "AMX-TILE: tile architecture support": "true", # CPUID.(EAX=07H,ECX=0):EDX[24] + "AMX-INT8: tile 8-bit integer support": "true", # CPUID.(EAX=07H,ECX=0):EDX[25] + "AMX-FP16: FP16 tile operations": "false", # CPUID.(EAX=07H,ECX=1):EAX[21], not supported on host as well + "XTILECFG state": "true", # CPUID.(EAX=0DH,ECX=0):EAX[17] + "XTILEDATA state": "true", # CPUID.(EAX=0DH,ECX=0):EAX[17] + } + cpuid_utils.check_guest_cpuid_output( + uvm, + "cpuid -1", + None, + "=", + expected_dict, + ) diff --git a/tests/integration_tests/functional/test_cpu_template_helper.py b/tests/integration_tests/functional/test_cpu_template_helper.py index 3e57cca644a..6ff2db1f7f0 100644 --- a/tests/integration_tests/functional/test_cpu_template_helper.py +++ b/tests/integration_tests/functional/test_cpu_template_helper.py @@ -133,6 +133,10 @@ def build_cpu_config_dict(cpu_config_path): # support it, the userspace cpuid command in ubuntu 22 reports not only # the subleaf 0 but also the subleaf 1. (0x1B, 0x1), + # CPUID.1Fh is a preferred superset to CPUID.0Bh. For the same reason as + # CPUID.Bh, the subleaf 2 should be skipped when the guest userspace cpuid + # enumerates it. + (0x1F, 0x2), # CPUID.20000000h is not documented in Intel SDM and AMD APM. KVM doesn't # report it, but the userspace cpuid command in ubuntu 22 does. (0x20000000, 0x0), @@ -146,13 +150,14 @@ def build_cpu_config_dict(cpu_config_path): # https://github.com/torvalds/linux/commit/8765d75329a386dd7742f94a1ea5fdcdea8d93d0 (0x8000001B, 0x0), (0x8000001C, 0x0), - (0x8000001F, 0x0), # CPUID.80860000h is a Transmeta-specific leaf. (0x80860000, 0x0), # CPUID.C0000000h is a Centaur-specific leaf. (0xC0000000, 0x0), ] +# An upper range of CPUID leaves which are not supported by our kernels +UNAVAILABLE_CPUID_UPPER_RANGE = range(0x8000001F, 0x80000029) # Dictionary of CPUID bitmasks that should not be tested due to its mutability. CPUID_EXCEPTION_LIST = { @@ -183,6 +188,9 @@ def build_cpu_config_dict(cpu_config_path): 0x48, # MSR_IA32_SMBASE is not accessible outside of System Management Mode. 0x9E, + # MSR_IA32_UMWAIT_CONTROL is R/W MSR that guest OS modifies after boot to + # control UMWAIT feature. + 0xE1, # MSR_IA32_TSX_CTRL is R/W MSR to disable Intel TSX feature as a mitigation # against TAA vulnerability. 0x122, @@ -192,6 +200,10 @@ def build_cpu_config_dict(cpu_config_path): 0x174, 0x175, 0x176, + # MSR_IA32_XFD is R/W MSR for guest OS to control which XSAVE-enabled + # features are temporarily disabled. Guest OS disables TILEDATA by default + # using the MSR. + 0x1C4, # MSR_IA32_TSC_DEADLINE specifies the time at which a timer interrupt # should occur and depends on the elapsed time. 0x6E0, @@ -281,6 +293,8 @@ def test_cpu_config_dump_vs_actual( for key, actual in actual_cpu_config["cpuid"].items(): if (key[0], key[1]) in UNAVAILABLE_CPUID_ON_DUMP_LIST: continue + if key[0] in UNAVAILABLE_CPUID_UPPER_RANGE: + continue if key not in dump_cpu_config["cpuid"]: keys_not_in_dump[key] = actual_cpu_config["cpuid"][key] continue diff --git a/tests/integration_tests/functional/test_drive_vhost_user.py b/tests/integration_tests/functional/test_drive_vhost_user.py index 31a11a75661..79cc41b0f3a 100644 --- a/tests/integration_tests/functional/test_drive_vhost_user.py +++ b/tests/integration_tests/functional/test_drive_vhost_user.py @@ -34,7 +34,7 @@ def _check_drives(test_microvm, assert_dict, keys_array): assert blockdev_out_line_cols[col] == assert_dict[key] -def test_vhost_user_block(microvm_factory, guest_kernel, rootfs_ubuntu_22): +def test_vhost_user_block(microvm_factory, guest_kernel, rootfs): """ This test simply tries to boot a VM with vhost-user-block as a root device. @@ -44,13 +44,11 @@ def test_vhost_user_block(microvm_factory, guest_kernel, rootfs_ubuntu_22): # We need to setup ssh keys manually because we did not specify rootfs # in microvm_factory.build method - ssh_key = rootfs_ubuntu_22.with_suffix(".id_rsa") + ssh_key = rootfs.with_suffix(".id_rsa") vm.ssh_key = ssh_key vm.spawn() vm.basic_config(add_root_device=False) - vm.add_vhost_user_drive( - "rootfs", rootfs_ubuntu_22, is_root_device=True, is_read_only=True - ) + vm.add_vhost_user_drive("rootfs", rootfs, is_root_device=True, is_read_only=True) vm.add_net_iface() vhost_user_block_metrics = FcDeviceMetrics( "vhost_user_block", 1, aggr_supported=False @@ -67,7 +65,7 @@ def test_vhost_user_block(microvm_factory, guest_kernel, rootfs_ubuntu_22): vhost_user_block_metrics.validate(vm) -def test_vhost_user_block_read_write(microvm_factory, guest_kernel, rootfs_ubuntu_22): +def test_vhost_user_block_read_write(microvm_factory, guest_kernel, rootfs): """ This test simply tries to boot a VM with vhost-user-block as a root device. @@ -78,14 +76,14 @@ def test_vhost_user_block_read_write(microvm_factory, guest_kernel, rootfs_ubunt # We need to setup ssh keys manually because we did not specify rootfs # in microvm_factory.build method - ssh_key = rootfs_ubuntu_22.with_suffix(".id_rsa") + ssh_key = rootfs.with_suffix(".id_rsa") vm.ssh_key = ssh_key vm.spawn() vm.basic_config(add_root_device=False) # Create a rw rootfs file that is unique to the microVM rootfs_rw = Path(vm.chroot()) / "rootfs" - shutil.copy(rootfs_ubuntu_22, rootfs_rw) + shutil.copy(rootfs, rootfs_rw) vm.add_vhost_user_drive("rootfs", rootfs_rw, is_root_device=True) vm.add_net_iface() @@ -100,7 +98,7 @@ def test_vhost_user_block_read_write(microvm_factory, guest_kernel, rootfs_ubunt _check_drives(vm, assert_dict, assert_dict.keys()) -def test_vhost_user_block_disconnect(microvm_factory, guest_kernel, rootfs_ubuntu_22): +def test_vhost_user_block_disconnect(microvm_factory, guest_kernel, rootfs): """ Test that even if backend is killed, Firecracker is still responsive. """ @@ -109,13 +107,11 @@ def test_vhost_user_block_disconnect(microvm_factory, guest_kernel, rootfs_ubunt # We need to set up ssh keys manually because we did not specify rootfs # in microvm_factory.build method - ssh_key = rootfs_ubuntu_22.with_suffix(".id_rsa") + ssh_key = rootfs.with_suffix(".id_rsa") vm.ssh_key = ssh_key vm.spawn() vm.basic_config(add_root_device=False) - vm.add_vhost_user_drive( - "rootfs", rootfs_ubuntu_22, is_root_device=True, is_read_only=True - ) + vm.add_vhost_user_drive("rootfs", rootfs, is_root_device=True, is_read_only=True) vm.add_net_iface() vm.start() @@ -127,7 +123,7 @@ def test_vhost_user_block_disconnect(microvm_factory, guest_kernel, rootfs_ubunt _config = vm.api.vm_config.get().json() -def test_device_ordering(microvm_factory, guest_kernel, rootfs_ubuntu_22): +def test_device_ordering(microvm_factory, guest_kernel, rootfs): """ Verify device ordering. @@ -139,7 +135,7 @@ def test_device_ordering(microvm_factory, guest_kernel, rootfs_ubuntu_22): # We need to setup ssh keys manually because we did not specify rootfs # in microvm_factory.build method - ssh_key = rootfs_ubuntu_22.with_suffix(".id_rsa") + ssh_key = rootfs.with_suffix(".id_rsa") vm.ssh_key = ssh_key vm.spawn() vm.basic_config(add_root_device=False) @@ -150,9 +146,7 @@ def test_device_ordering(microvm_factory, guest_kernel, rootfs_ubuntu_22): vm.add_drive("scratch1", fs1.path) # Adding second block device (rootfs) - vm.add_vhost_user_drive( - "rootfs", rootfs_ubuntu_22, is_root_device=True, is_read_only=True - ) + vm.add_vhost_user_drive("rootfs", rootfs, is_root_device=True, is_read_only=True) # Adding third block device. fs2 = drive_tools.FilesystemFile(os.path.join(vm.fsfiles, "scratch2"), size=512) @@ -160,7 +154,7 @@ def test_device_ordering(microvm_factory, guest_kernel, rootfs_ubuntu_22): # Create a rw rootfs file that is unique to the microVM rootfs_rw = Path(vm.chroot()) / "rootfs" - shutil.copy(rootfs_ubuntu_22, rootfs_rw) + shutil.copy(rootfs, rootfs_rw) # Adding forth block device. vm.add_vhost_user_drive("dummy_rootfs", rootfs_rw) @@ -171,7 +165,7 @@ def test_device_ordering(microvm_factory, guest_kernel, rootfs_ubuntu_22): ) vm.start() - rootfs_size = rootfs_ubuntu_22.stat().st_size + rootfs_size = rootfs.stat().st_size # The devices were added in this order: fs1, rootfs, fs2. fs3 # However, the rootfs is the root device and goes first, @@ -203,7 +197,7 @@ def test_device_ordering(microvm_factory, guest_kernel, rootfs_ubuntu_22): def test_partuuid_boot( microvm_factory, guest_kernel, - rootfs_ubuntu_22, + rootfs, ): """ Test the output reported by blockdev when booting with PARTUUID. @@ -213,15 +207,13 @@ def test_partuuid_boot( # We need to setup ssh keys manually because we did not specify rootfs # in microvm_factory.build method - ssh_key = rootfs_ubuntu_22.with_suffix(".id_rsa") + ssh_key = rootfs.with_suffix(".id_rsa") vm.ssh_key = ssh_key vm.spawn() vm.basic_config(add_root_device=False) # Create a rootfs with partuuid unique to this microVM - partuuid, disk_path = partuuid_and_disk_path( - rootfs_ubuntu_22, Path(vm.chroot()) / "disk.img" - ) + partuuid, disk_path = partuuid_and_disk_path(rootfs, Path(vm.chroot()) / "disk.img") vm.add_vhost_user_drive( "1", disk_path, is_root_device=True, partuuid=partuuid, is_read_only=True @@ -238,7 +230,7 @@ def test_partuuid_boot( _check_drives(vm, assert_dict, assert_dict.keys()) -def test_partuuid_update(microvm_factory, guest_kernel, rootfs_ubuntu_22): +def test_partuuid_update(microvm_factory, guest_kernel, rootfs): """ Test successful switching from PARTUUID boot to /dev/vda boot. """ @@ -247,7 +239,7 @@ def test_partuuid_update(microvm_factory, guest_kernel, rootfs_ubuntu_22): # We need to setup ssh keys manually because we did not specify rootfs # in microvm_factory.build method - ssh_key = rootfs_ubuntu_22.with_suffix(".id_rsa") + ssh_key = rootfs.with_suffix(".id_rsa") vm.ssh_key = ssh_key vm.spawn() vm.basic_config(add_root_device=False) @@ -256,16 +248,14 @@ def test_partuuid_update(microvm_factory, guest_kernel, rootfs_ubuntu_22): # Add the root block device specified through PARTUUID. vm.add_vhost_user_drive( "rootfs", - rootfs_ubuntu_22, + rootfs, is_root_device=True, partuuid="0eaa91a0-01", is_read_only=True, ) # Adding a drive with the same ID creates another backend with another socket. - vm.add_vhost_user_drive( - "rootfs", rootfs_ubuntu_22, is_root_device=True, is_read_only=True - ) + vm.add_vhost_user_drive("rootfs", rootfs, is_root_device=True, is_read_only=True) vhost_user_block_metrics = FcDeviceMetrics( "vhost_user_block", 1, aggr_supported=False diff --git a/tests/integration_tests/functional/test_drive_virtio.py b/tests/integration_tests/functional/test_drive_virtio.py index dbebefa11a1..9c61ead56a9 100644 --- a/tests/integration_tests/functional/test_drive_virtio.py +++ b/tests/integration_tests/functional/test_drive_virtio.py @@ -14,12 +14,12 @@ @pytest.fixture -def partuuid_and_disk_path_tmpfs(rootfs_ubuntu_22, tmp_path): +def partuuid_and_disk_path_tmpfs(rootfs, tmp_path): """ We create a new file in tmpfs, get its partuuid and use it as a rootfs. """ disk_path = tmp_path / "disk.img" - yield partuuid_and_disk_path(rootfs_ubuntu_22, disk_path) + yield partuuid_and_disk_path(rootfs, disk_path) disk_path.unlink() diff --git a/tests/integration_tests/functional/test_kernel_cmdline.py b/tests/integration_tests/functional/test_kernel_cmdline.py index 14e369790f1..9707eb8a92c 100644 --- a/tests/integration_tests/functional/test_kernel_cmdline.py +++ b/tests/integration_tests/functional/test_kernel_cmdline.py @@ -29,4 +29,4 @@ def test_init_params(uvm_plain): serial = Serial(vm) serial.open() # If the string does not show up, the test will fail. - serial.rx(token="Ubuntu 22.04") + serial.rx(token="Ubuntu 24.04") diff --git a/tests/integration_tests/functional/test_kvm_ptp.py b/tests/integration_tests/functional/test_kvm_ptp.py index 70b5bb877bc..4b44ca124eb 100644 --- a/tests/integration_tests/functional/test_kvm_ptp.py +++ b/tests/integration_tests/functional/test_kvm_ptp.py @@ -6,19 +6,18 @@ import pytest -def test_kvm_ptp(uvm_plain_any): +def test_kvm_ptp(uvm_any_booted): """Test kvm_ptp is usable""" - vm = uvm_plain_any + vm = uvm_any_booted if vm.guest_kernel_version[:2] < (6, 1): pytest.skip("Only supported in kernel 6.1 and after") - vm.spawn() - vm.basic_config(vcpu_count=2, mem_size_mib=256) - vm.add_net_iface() - vm.start() + _, dmesg, _ = vm.ssh.check_output("dmesg |grep -i ptp") + assert "PTP clock support registered" in dmesg - vm.ssh.check_output("[ -c /dev/ptp0 ]") + # wait up to 5s to see the PTP device + vm.ssh.check_output("udevadm wait -t 5 /dev/ptp0") # phc_ctl[14515.127]: clock time is 1697545854.728335694 or Tue Oct 17 12:30:54 2023 vm.ssh.check_output("phc_ctl /dev/ptp0 -- get") diff --git a/tests/integration_tests/functional/test_max_vcpus.py b/tests/integration_tests/functional/test_max_vcpus.py deleted file mode 100644 index 05c1f3c51ff..00000000000 --- a/tests/integration_tests/functional/test_max_vcpus.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -"""Tests scenario for microvms with max vcpus(32).""" - -MAX_VCPUS = 32 - - -def test_max_vcpus(uvm_plain): - """ - Test if all configured guest vcpus are online. - """ - microvm = uvm_plain - microvm.spawn() - - # Configure a microVM with 32 vCPUs. - microvm.basic_config(vcpu_count=MAX_VCPUS) - microvm.add_net_iface() - microvm.start() - - cmd = "nproc" - _, stdout, stderr = microvm.ssh.run(cmd) - assert stderr == "" - assert int(stdout) == MAX_VCPUS diff --git a/tests/integration_tests/functional/test_net.py b/tests/integration_tests/functional/test_net.py index 12980c727b2..20a40e677b0 100644 --- a/tests/integration_tests/functional/test_net.py +++ b/tests/integration_tests/functional/test_net.py @@ -10,7 +10,11 @@ from framework import utils # The iperf version to run this tests with -IPERF_BINARY = "iperf3" +IPERF_BINARY_GUEST = "iperf3" +# We are using iperf3-vsock instead of a regular iperf3, +# because iperf3 3.16+ crashes on aarch64 sometimes +# when running this test. +IPERF_BINARY_HOST = "iperf3-vsock" def test_high_ingress_traffic(uvm_plain_any): @@ -33,15 +37,15 @@ def test_high_ingress_traffic(uvm_plain_any): test_microvm.start() # Start iperf3 server on the guest. - test_microvm.ssh.run("{} -sD\n".format(IPERF_BINARY)) + test_microvm.ssh.check_output("{} -sD\n".format(IPERF_BINARY_GUEST)) time.sleep(1) # Start iperf3 client on the host. Send 1Gbps UDP traffic. - # If the net device breaks, iperf will freeze. We have to use a timeout. - utils.run_cmd( - "timeout 30 {} {} -c {} -u -V -b 1000000000 -t 30".format( + # If the net device breaks, iperf will freeze, and we'll hit the pytest timeout + utils.check_output( + "{} {} -c {} -u -V -b 1000000000 -t 30".format( test_microvm.netns.cmd_prefix(), - IPERF_BINARY, + IPERF_BINARY_HOST, guest_ip, ), ) @@ -79,15 +83,27 @@ def test_multi_queue_unsupported(uvm_plain): guest_mac="AA:FC:00:00:00:01", ) + # clean TAP device + utils.run_cmd(f"{microvm.netns.cmd_prefix()} ip link del name {tapname}") -def run_udp_offload_test(vm): + +@pytest.fixture +def uvm_any(microvm_factory, uvm_ctor, guest_kernel, rootfs): + """Return booted and restored uvm with no CPU templates""" + return uvm_ctor(microvm_factory, guest_kernel, rootfs, None) + + +def test_tap_offload(uvm_any): """ + Verify that tap offload features are configured for a booted/restored VM. + - Start a socat UDP server in the guest. - Try to send a UDP message with UDP offload enabled. If tap offload features are not configured, an attempt to send a message will fail with EIO "Input/output error". More info (search for "TUN_F_CSUM is a must"): https://blog.cloudflare.com/fr-fr/virtual-networking-101-understanding-tap/ """ + vm = uvm_any port = "81" out_filename = "/tmp/out.txt" message = "x" @@ -95,48 +111,12 @@ def run_udp_offload_test(vm): # Start a UDP server in the guest # vm.ssh.check_output(f"nohup socat UDP-LISTEN:{port} - > {out_filename} &") vm.ssh.check_output( - f"nohup socat UDP-LISTEN:{port} OPEN:{out_filename},creat > /dev/null 2>&1 &" + f"nohup socat UDP4-LISTEN:{port} OPEN:{out_filename},creat > /dev/null 2>&1 &" ) # Try to send a UDP message from host with UDP offload enabled - cmd = f"ip netns exec {vm.ssh_iface().netns} python3 ./host_tools/udp_offload.py {vm.ssh_iface().host} {port}" - ret = utils.run_cmd(cmd) - - # Check that the transmission was successful - assert ret.returncode == 0, f"{ret.stdout=} {ret.stderr=}" + vm.netns.check_output(f"python3 ./host_tools/udp_offload.py {vm.ssh.host} {port}") # Check that the server received the message - ret = vm.ssh.run(f"cat {out_filename}") + ret = vm.ssh.run(f"sync ; cat {out_filename}") assert ret.stdout == message, f"{ret.stdout=} {ret.stderr=}" - - -def test_tap_offload_booted(uvm_plain_any): - """ - Verify that tap offload features are configured for a booted VM. - """ - vm = uvm_plain_any - vm.spawn() - vm.basic_config() - vm.add_net_iface() - vm.start() - - run_udp_offload_test(vm) - - -def test_tap_offload_restored(microvm_factory, guest_kernel, rootfs_ubuntu_22): - """ - Verify that tap offload features are configured for a restored VM. - """ - src = microvm_factory.build(guest_kernel, rootfs_ubuntu_22, monitor_memory=False) - src.spawn() - src.basic_config() - src.add_net_iface() - src.start() - snapshot = src.snapshot_full() - src.kill() - - dst = microvm_factory.build() - dst.spawn() - dst.restore_from_snapshot(snapshot, resume=True) - - run_udp_offload_test(dst) diff --git a/tests/integration_tests/functional/test_pause_resume.py b/tests/integration_tests/functional/test_pause_resume.py index 3d0ac124c11..ca2bb6936b3 100644 --- a/tests/integration_tests/functional/test_pause_resume.py +++ b/tests/integration_tests/functional/test_pause_resume.py @@ -4,6 +4,7 @@ import platform import time +from subprocess import TimeoutExpired import pytest @@ -52,17 +53,13 @@ def test_pause_resume(uvm_nano): microvm.flush_metrics() # Verify guest is no longer active. - with pytest.raises(ChildProcessError): - microvm.ssh.check_output("true") + with pytest.raises(TimeoutExpired): + microvm.ssh.check_output("true", timeout=1) # Verify emulation was indeed paused and no events from either # guest or host side were handled. verify_net_emulation_paused(microvm.flush_metrics()) - # Verify guest is no longer active. - with pytest.raises(ChildProcessError): - microvm.ssh.check_output("true") - # Pausing the microVM when it is already `Paused` is allowed # (microVM remains in `Paused` state). microvm.api.vm.patch(state="Paused") @@ -71,6 +68,7 @@ def test_pause_resume(uvm_nano): microvm.api.vm.patch(state="Resumed") # Verify guest is active again. + microvm.ssh.check_output("true") # Resuming the microVM when it is already `Resumed` is allowed # (microVM remains in the running state). @@ -143,7 +141,12 @@ def test_kvmclock_ctrl(uvm_plain_any): microvm = uvm_plain_any microvm.help.enable_console() microvm.spawn() - microvm.basic_config() + + # With 2 vCPUs under certain conditions soft lockup warnings can rarely be in dmesg causing this test to fail. + # Example of the warning: `watchdog: BUG: soft lockup - CPU#0 stuck for (x)s! [(udev-worker):758]` + # With 1 vCPU this intermittent issue doesn't occur. If the KVM_CLOCK_CTRL IOCTL is not made + # the test will fail with 1 vCPU, so we can assert the call to the IOCTL is made. + microvm.basic_config(vcpu_count=1) microvm.add_net_iface() microvm.start() diff --git a/tests/integration_tests/functional/test_rng.py b/tests/integration_tests/functional/test_rng.py index b40aa66033d..1893230c51a 100644 --- a/tests/integration_tests/functional/test_rng.py +++ b/tests/integration_tests/functional/test_rng.py @@ -8,11 +8,9 @@ from host_tools.network import SSHConnection -@pytest.fixture(params=[None]) -def uvm_with_rng(uvm_plain, request): - """Fixture of a microvm with virtio-rng configured""" - rate_limiter = request.param - uvm = uvm_plain +def uvm_with_rng_booted(microvm_factory, guest_kernel, rootfs, rate_limiter): + """Return a booted microvm with virtio-rng configured""" + uvm = microvm_factory.build(guest_kernel, rootfs) uvm.spawn(log_level="INFO") uvm.basic_config(vcpu_count=2, mem_size_mib=256) uvm.add_net_iface() @@ -23,6 +21,34 @@ def uvm_with_rng(uvm_plain, request): return uvm +def uvm_with_rng_restored(microvm_factory, guest_kernel, rootfs, rate_limiter): + """Return a restored uvm with virtio-rng configured""" + uvm = uvm_with_rng_booted(microvm_factory, guest_kernel, rootfs, rate_limiter) + snapshot = uvm.snapshot_full() + uvm.kill() + uvm2 = microvm_factory.build_from_snapshot(snapshot) + uvm2.rng_rate_limiter = uvm.rng_rate_limiter + return uvm2 + + +@pytest.fixture(params=[uvm_with_rng_booted, uvm_with_rng_restored]) +def uvm_ctor(request): + """Fixture to return uvms with different constructors""" + return request.param + + +@pytest.fixture(params=[None]) +def rate_limiter(request): + """Fixture to return different rate limiters""" + return request.param + + +@pytest.fixture +def uvm_any(microvm_factory, uvm_ctor, guest_kernel, rootfs, rate_limiter): + """Return booted and restored uvms""" + return uvm_ctor(microvm_factory, guest_kernel, rootfs, rate_limiter) + + def list_rng_available(ssh_connection: SSHConnection) -> list[str]: """Returns a list of rng devices available in the VM""" return ( @@ -62,35 +88,17 @@ def test_rng_not_present(uvm_nano): ), "virtio_rng device should not be available in the uvm" -def test_rng_present(uvm_with_rng): +def test_rng_present(uvm_any): """ Test a guest microVM with an entropy defined configured and ensure that we can access `/dev/hwrng` """ - vm = uvm_with_rng + vm = uvm_any assert_virtio_rng_is_current_hwrng_device(vm.ssh) check_entropy(vm.ssh) -def test_rng_snapshot(uvm_with_rng, microvm_factory): - """ - Test that a virtio-rng device is functional after resuming from - a snapshot - """ - - vm = uvm_with_rng - assert_virtio_rng_is_current_hwrng_device(vm.ssh) - check_entropy(vm.ssh) - snapshot = vm.snapshot_full() - - new_vm = microvm_factory.build() - new_vm.spawn() - new_vm.restore_from_snapshot(snapshot, resume=True) - assert_virtio_rng_is_current_hwrng_device(new_vm.ssh) - check_entropy(new_vm.ssh) - - def _get_percentage_difference(measured, base): """Return the percentage delta between the arguments.""" if measured == base: @@ -199,7 +207,7 @@ def _rate_limiter_id(rate_limiter): # parametrize the RNG rate limiter @pytest.mark.parametrize( - "uvm_with_rng", + "rate_limiter", [ {"bandwidth": {"size": 1000, "refill_time": 100}}, {"bandwidth": {"size": 10000, "refill_time": 100}}, @@ -208,16 +216,14 @@ def _rate_limiter_id(rate_limiter): indirect=True, ids=_rate_limiter_id, ) -def test_rng_bw_rate_limiter(uvm_with_rng): +@pytest.mark.parametrize("uvm_ctor", [uvm_with_rng_booted], indirect=True) +def test_rng_bw_rate_limiter(uvm_any): """ Test that rate limiter without initial burst budget works """ - vm = uvm_with_rng - # _start_vm_with_rng(vm, rate_limiter) - + vm = uvm_any size = vm.rng_rate_limiter["bandwidth"]["size"] refill_time = vm.rng_rate_limiter["bandwidth"]["refill_time"] - expected_kbps = size / refill_time assert_virtio_rng_is_current_hwrng_device(vm.ssh) diff --git a/tests/integration_tests/functional/test_serial_io.py b/tests/integration_tests/functional/test_serial_io.py index db1521d4a44..aee9047f531 100644 --- a/tests/integration_tests/functional/test_serial_io.py +++ b/tests/integration_tests/functional/test_serial_io.py @@ -16,7 +16,7 @@ PLATFORM = platform.machine() -class WaitTerminal(TestState): # pylint: disable=too-few-public-methods +class WaitTerminal(TestState): """Initial state when we wait for the login prompt.""" def handle_input(self, serial, input_char) -> TestState: @@ -27,7 +27,7 @@ def handle_input(self, serial, input_char) -> TestState: return self -class WaitIDResult(TestState): # pylint: disable=too-few-public-methods +class WaitIDResult(TestState): """Wait for the console to show the result of the 'id' shell command.""" def handle_input(self, unused_serial, input_char) -> TestState: @@ -37,7 +37,7 @@ def handle_input(self, unused_serial, input_char) -> TestState: return self -class TestFinished(TestState): # pylint: disable=too-few-public-methods +class TestFinished(TestState): """Test complete and successful.""" def handle_input(self, unused_serial, _) -> TestState: diff --git a/tests/integration_tests/functional/test_snapshot_basic.py b/tests/integration_tests/functional/test_snapshot_basic.py index ac596440f67..deb9b2dd6c3 100644 --- a/tests/integration_tests/functional/test_snapshot_basic.py +++ b/tests/integration_tests/functional/test_snapshot_basic.py @@ -2,18 +2,25 @@ # SPDX-License-Identifier: Apache-2.0 """Basic tests scenarios for snapshot save/restore.""" +import dataclasses import filecmp import logging import os +import platform import re import shutil import time +import uuid from pathlib import Path import pytest +import host_tools.cargo_build as host import host_tools.drive as drive_tools +import host_tools.network as net_tools +from framework import utils from framework.microvm import SnapshotType +from framework.properties import global_props from framework.utils import check_filesystem, check_output from framework.utils_vsock import ( ECHO_SERVER_PORT, @@ -50,39 +57,25 @@ def _get_guest_drive_size(ssh_connection, guest_dev_name="/dev/vdb"): return lines[1].strip() -def test_resume_after_restoration(uvm_nano, microvm_factory): - """Tests snapshot is resumable after restoration. +@pytest.mark.parametrize("resume_at_restore", [True, False]) +def test_resume(uvm_nano, microvm_factory, resume_at_restore): + """Tests snapshot is resumable at or after restoration. - Check that a restored microVM is resumable by calling PATCH /vm with Resumed - after PUT /snapshot/load with `resume_vm=False`. + Check that a restored microVM is resumable by either + a. PUT /snapshot/load with `resume_vm=False`, then calling PATCH /vm resume=True + b. PUT /snapshot/load with `resume_vm=True` """ vm = uvm_nano vm.add_net_iface() vm.start() - - snapshot = vm.snapshot_full() - - restored_vm = microvm_factory.build() - restored_vm.spawn() - restored_vm.restore_from_snapshot(snapshot) - restored_vm.resume() - - -def test_resume_at_restoration(uvm_nano, microvm_factory): - """Tests snapshot is resumable at restoration. - - Check that a restored microVM is resumable by calling PUT /snapshot/load - with `resume_vm=True`. - """ - vm = uvm_nano - vm.add_net_iface() - vm.start() - snapshot = vm.snapshot_full() - restored_vm = microvm_factory.build() restored_vm.spawn() - restored_vm.restore_from_snapshot(snapshot, resume=True) + restored_vm.restore_from_snapshot(snapshot, resume=resume_at_restore) + if not resume_at_restore: + assert restored_vm.state == "Paused" + restored_vm.resume() + assert restored_vm.state == "Running" def test_snapshot_current_version(uvm_nano): @@ -120,7 +113,7 @@ def test_snapshot_current_version(uvm_nano): # TODO: Multiple microvm sizes must be tested in the async pipeline. @pytest.mark.parametrize("snapshot_type", [SnapshotType.DIFF, SnapshotType.FULL]) @pytest.mark.parametrize("use_snapshot_editor", [False, True]) -def test_5_snapshots( +def test_cycled_snapshot_restore( bin_vsock_path, tmp_path, microvm_factory, @@ -128,12 +121,17 @@ def test_5_snapshots( rootfs, snapshot_type, use_snapshot_editor, + cpu_template_any, ): """ - Create and load 5 snapshots. + Run a cycle of VM restoration and VM snapshot creation where new VM is + restored from a snapshot of the previous one. """ + # This is an arbitrary selected value. It is big enough to test the + # functionality, but small enough to not be annoying long to run. + cycles = 3 + logger = logging.getLogger("snapshot_sequence") - seq_len = 5 diff_snapshots = snapshot_type == SnapshotType.DIFF vm = microvm_factory.build(guest_kernel, rootfs) @@ -143,6 +141,7 @@ def test_5_snapshots( mem_size_mib=512, track_dirty_pages=diff_snapshots, ) + vm.set_cpu_template(cpu_template_any) vm.add_net_iface() vm.api.vsock.put(vsock_id="vsock0", guest_cid=3, uds_path=VSOCK_UDS_PATH) vm.start() @@ -159,12 +158,9 @@ def test_5_snapshots( snapshot = vm.make_snapshot(snapshot_type) vm.kill() - for i in range(seq_len): - logger.info("Load snapshot #%s, mem %s", i, snapshot.mem) - microvm = microvm_factory.build() - microvm.spawn() - copied_snapshot = microvm.restore_from_snapshot(snapshot, resume=True) - + for microvm in microvm_factory.build_n_from_snapshot( + snapshot, cycles, incremental=True, use_snapshot_editor=use_snapshot_editor + ): # FIXME: This and the sleep below reduce the rate of vsock/ssh connection # related spurious test failures, although we do not know why this is the case. time.sleep(2) @@ -181,21 +177,6 @@ def test_5_snapshots( check_filesystem(microvm.ssh, "squashfs", "/dev/vda") time.sleep(2) - logger.info("Create snapshot %s #%d.", snapshot_type, i + 1) - new_snapshot = microvm.make_snapshot(snapshot_type) - - # If we are testing incremental snapshots we must merge the base with - # current layer. - if snapshot.is_diff: - logger.info("Base: %s, Layer: %s", snapshot.mem, new_snapshot.mem) - new_snapshot = new_snapshot.rebase_snapshot( - snapshot, use_snapshot_editor=use_snapshot_editor - ) - - microvm.kill() - copied_snapshot.delete() - # Update the base for next iteration. - snapshot = new_snapshot def test_patch_drive_snapshot(uvm_nano, microvm_factory): @@ -228,9 +209,7 @@ def test_patch_drive_snapshot(uvm_nano, microvm_factory): # Load snapshot in a new Firecracker microVM. logger.info("Load snapshot, mem %s", snapshot.mem) - vm = microvm_factory.build() - vm.spawn() - vm.restore_from_snapshot(snapshot, resume=True) + vm = microvm_factory.build_from_snapshot(snapshot) # Attempt to connect to resumed microvm and verify the new microVM has the # right scratch drive. @@ -319,9 +298,7 @@ def test_negative_postload_api(uvm_plain, microvm_factory): basevm.kill() # Do not resume, just load, so we can still call APIs that work. - microvm = microvm_factory.build() - microvm.spawn() - microvm.restore_from_snapshot(snapshot, resume=True) + microvm = microvm_factory.build_from_snapshot(snapshot) fail_msg = "The requested operation is not supported after starting the microVM" with pytest.raises(RuntimeError, match=fail_msg): @@ -486,9 +463,7 @@ def test_diff_snapshot_overlay(guest_kernel, rootfs, microvm_factory): assert not filecmp.cmp(merged_snapshot.mem, first_snapshot_backup, shallow=False) - new_vm = microvm_factory.build() - new_vm.spawn() - new_vm.restore_from_snapshot(merged_snapshot, resume=True) + _ = microvm_factory.build_from_snapshot(merged_snapshot) # Check that the restored VM works @@ -510,9 +485,7 @@ def test_snapshot_overwrite_self(guest_kernel, rootfs, microvm_factory): snapshot = base_vm.snapshot_full() base_vm.kill() - vm = microvm_factory.build() - vm.spawn() - vm.restore_from_snapshot(snapshot, resume=True) + vm = microvm_factory.build_from_snapshot(snapshot) # When restoring a snapshot, vm.restore_from_snapshot first copies # the memory file (inside of the jailer) to /mem.src @@ -542,23 +515,93 @@ def test_vmgenid(guest_kernel_linux_6_1, rootfs, microvm_factory, snapshot_type) base_snapshot = snapshot base_vm.kill() - for i in range(5): - vm = microvm_factory.build() - vm.spawn() - copied_snapshot = vm.restore_from_snapshot(snapshot, resume=True) - + for i, vm in enumerate( + microvm_factory.build_n_from_snapshot(base_snapshot, 5, incremental=True) + ): # We should have as DMESG_VMGENID_RESUME messages as # snapshots we have resumed check_vmgenid_update_count(vm, i + 1) - snapshot = vm.make_snapshot(snapshot_type) - vm.kill() - copied_snapshot.delete() - # If we are testing incremental snapshots we ust merge the base with - # current layer. - if snapshot.is_diff: - snapshot = snapshot.rebase_snapshot(base_snapshot) +@pytest.mark.skipif( + platform.machine() != "aarch64" + or ( + global_props.host_linux_version_tpl < (6, 4) + and global_props.host_os not in ("amzn2", "amzn2023") + ), + reason="This test requires aarch64 and either kernel 6.4+ or Amazon Linux", +) +def test_physical_counter_reset_aarch64(uvm_nano): + """ + Test that the CNTPCT_EL0 register is reset on VM boot. + We assume the smallest VM will not consume more than + some MAX_VALUE cycles to be created and snapshotted. + The MAX_VALUE is selected by doing a manual run of this test and + seeing what the actual counter value is. The assumption here is that + if resetting will not occur the guest counter value will be huge as it + will be a copy of host value. The host value in its turn will be huge because + it will include host OS boot + CI prep + other CI tests ... + """ + vm = uvm_nano + vm.add_net_iface() + vm.start() + + snapshot = vm.snapshot_full() + vm.kill() + snap_editor = host.get_binary("snapshot-editor") + + cntpct_el0 = hex(0x603000000013DF01) + # If a CPU runs at 3GHz, it will have a counter value of 8_000_000_000 + # in 2.66 seconds. The host surely will run for more than 2.66 seconds before + # executing this test. + max_value = 8_000_000_000 + + cmd = [ + str(snap_editor), + "info-vmstate", + "vcpu-states", + "--vmstate-path", + str(snapshot.vmstate), + ] + _, stdout, _ = utils.check_output(cmd) + + # The output will look like this: + # kvm_mp_state: 0x0 + # mpidr: 0x80000000 + # 0x6030000000100000 0x0000000e0 + # 0x6030000000100002 0xffff00fe33c0 + for line in stdout.splitlines(): + parts = line.split() + if len(parts) == 2: + reg_id, reg_value = parts + if reg_id == cntpct_el0: + assert int(reg_value, 16) < max_value + break + else: + raise RuntimeError("Did not find CNTPCT_EL0 register in snapshot") + + +def test_snapshot_rename_interface(uvm_nano, microvm_factory): + """ + Test that we can restore a snapshot and point its interface to a + different host interface. + """ + vm = uvm_nano + base_iface = vm.add_net_iface() + vm.start() + snapshot = vm.snapshot_full() + + # We don't reuse the network namespace as it may conflict with + # previous/future devices + restored_vm = microvm_factory.build(netns=net_tools.NetNs(str(uuid.uuid4()))) + # Override the tap name, but keep the same IP configuration + iface_override = dataclasses.replace(base_iface, tap_name="tap_override") - # Update the base for next iteration - base_snapshot = snapshot + restored_vm.spawn() + snapshot.net_ifaces.clear() + snapshot.net_ifaces.append(iface_override) + restored_vm.restore_from_snapshot( + snapshot, + rename_interfaces={iface_override.dev_name: iface_override.tap_name}, + resume=True, + ) diff --git a/tests/integration_tests/functional/test_snapshot_editor.py b/tests/integration_tests/functional/test_snapshot_editor.py index 4d466a441ce..9323695628c 100644 --- a/tests/integration_tests/functional/test_snapshot_editor.py +++ b/tests/integration_tests/functional/test_snapshot_editor.py @@ -68,6 +68,4 @@ def test_remove_regs(uvm_nano, microvm_factory): assert MIDR_EL1 not in stdout # test that we can restore from a snapshot - new_vm = microvm_factory.build() - new_vm.spawn() - new_vm.restore_from_snapshot(snapshot, resume=True) + _ = microvm_factory.build_from_snapshot(snapshot) diff --git a/tests/integration_tests/functional/test_snapshot_not_losing_dirty_pages.py b/tests/integration_tests/functional/test_snapshot_not_losing_dirty_pages.py index 812e706b926..79366f13f0b 100644 --- a/tests/integration_tests/functional/test_snapshot_not_losing_dirty_pages.py +++ b/tests/integration_tests/functional/test_snapshot_not_losing_dirty_pages.py @@ -25,14 +25,14 @@ def mount_tmpfs_small(worker_id): def test_diff_snapshot_works_after_error( - microvm_factory, guest_kernel_linux_5_10, rootfs_ubuntu_22, mount_tmpfs_small + microvm_factory, guest_kernel_linux_5_10, rootfs, mount_tmpfs_small ): """ Test that if a partial snapshot errors it will work after and not lose data """ uvm = microvm_factory.build( guest_kernel_linux_5_10, - rootfs_ubuntu_22, + rootfs, jailer_kwargs={"chroot_base": mount_tmpfs_small}, ) @@ -63,9 +63,6 @@ def test_diff_snapshot_works_after_error( # Now there is enough space for it to work snap2 = uvm.snapshot_diff() - - vm2 = microvm_factory.build() - vm2.spawn() - vm2.restore_from_snapshot(snap2, resume=True) - uvm.kill() + + _vm2 = microvm_factory.build_from_snapshot(snap2) diff --git a/tests/integration_tests/functional/test_snapshot_phase1.py b/tests/integration_tests/functional/test_snapshot_phase1.py new file mode 100644 index 00000000000..7436c19d875 --- /dev/null +++ b/tests/integration_tests/functional/test_snapshot_phase1.py @@ -0,0 +1,108 @@ +# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Creates snapshots for other tests like test_snapshot_restore_cross_kernel.py +""" + +import json +import platform +import re + +import pytest + +from framework.utils import ( + configure_mmds, + generate_mmds_get_request, + generate_mmds_session_token, +) + +if platform.machine() != "x86_64": + pytestmark = pytest.mark.skip("only x86_64 architecture supported") + +# Default IPv4 address to route MMDS requests. +IPV4_ADDRESS = "169.254.169.254" +NET_IFACE_FOR_MMDS = "eth3" + + +@pytest.mark.nonci +def test_snapshot_phase1( + microvm_factory, guest_kernel, rootfs, cpu_template_any, results_dir +): + """Create a snapshot and save it to disk""" + + vm = microvm_factory.build(guest_kernel, rootfs, monitor_memory=False) + vm.spawn(log_level="Info") + vm.add_net_iface() + + static_cpu_template = None + cpu_template_name = "None" + if isinstance(cpu_template_any, str): + static_cpu_template = cpu_template_any + cpu_template_name = f"static_{cpu_template_any}" + elif isinstance(cpu_template_any, dict): + vm.api.cpu_config.put(**cpu_template_any["template"]) + cpu_template_name = f"custom_{cpu_template_any['name']}" + vm.basic_config( + vcpu_count=2, + mem_size_mib=512, + cpu_template=static_cpu_template, + ) + + guest_kernel_version = re.search("vmlinux-(.*)", vm.kernel_file.name) + snapshot_artifacts_dir = ( + results_dir + / f"{guest_kernel_version.group(1)}_{cpu_template_name}_guest_snapshot" + ) + + # Add 4 network devices + for i in range(4): + vm.add_net_iface() + # Add a vsock device + vm.api.vsock.put(vsock_id="vsock0", guest_cid=3, uds_path="/v.sock") + # Add MMDS + configure_mmds(vm, ["eth3"], version="V2") + # Add a memory balloon. + vm.api.balloon.put(amount_mib=0, deflate_on_oom=True, stats_polling_interval_s=1) + + vm.start() + + # Populate MMDS. + data_store = { + "latest": { + "meta-data": { + "ami-id": "ami-12345678", + "reservation-id": "r-fea54097", + "local-hostname": "ip-10-251-50-12.ec2.internal", + "public-hostname": "ec2-203-0-113-25.compute-1.amazonaws.com", + } + } + } + + # MMDS should be empty. + assert vm.api.mmds.get().json() == {} + # Populate MMDS with data. + vm.api.mmds.put(**data_store) + # Ensure data is persistent inside the data store. + assert vm.api.mmds.get().json() == data_store + + # Iterate and validate connectivity on all ifaces after boot. + for i in range(4): + exit_code, _, _ = vm.ssh_iface(i).run("sync") + assert exit_code == 0 + + # Validate MMDS. + # Configure interface to route MMDS requests + vm.ssh.check_output(f"ip route add {IPV4_ADDRESS} dev {NET_IFACE_FOR_MMDS}") + + # Fetch metadata to ensure MMDS is accessible. + token = generate_mmds_session_token(vm.ssh, IPV4_ADDRESS, token_ttl=60) + cmd = generate_mmds_get_request(IPV4_ADDRESS, token=token) + _, stdout, _ = vm.ssh.run(cmd) + assert json.loads(stdout) == data_store + + # Copy snapshot files to be published to S3 for the 2nd part of the test + # Create snapshot artifacts directory specific for the kernel version used. + snapshot = vm.snapshot_full() + snapshot_artifacts_dir.mkdir(parents=True) + snapshot.save_to(snapshot_artifacts_dir) diff --git a/tests/integration_tests/functional/test_uffd.py b/tests/integration_tests/functional/test_uffd.py index 44de52ed2d5..e003807d282 100644 --- a/tests/integration_tests/functional/test_uffd.py +++ b/tests/integration_tests/functional/test_uffd.py @@ -8,16 +8,15 @@ import pytest import requests -from framework.utils import Timeout, UffdHandler, check_output - -SOCKET_PATH = "/firecracker-uffd.sock" +from framework.utils import Timeout, check_output +from framework.utils_uffd import spawn_pf_handler, uffd_handler @pytest.fixture(scope="function", name="snapshot") -def snapshot_fxt(microvm_factory, guest_kernel_linux_5_10, rootfs_ubuntu_22): +def snapshot_fxt(microvm_factory, guest_kernel_linux_5_10, rootfs): """Create a snapshot of a microVM.""" - basevm = microvm_factory.build(guest_kernel_linux_5_10, rootfs_ubuntu_22) + basevm = microvm_factory.build(guest_kernel_linux_5_10, rootfs) basevm.spawn() basevm.basic_config(vcpu_count=2, mem_size_mib=256) basevm.add_net_iface() @@ -36,22 +35,6 @@ def snapshot_fxt(microvm_factory, guest_kernel_linux_5_10, rootfs_ubuntu_22): yield snapshot -def spawn_pf_handler(vm, handler_path, mem_path): - """Spawn page fault handler process.""" - # Copy snapshot memory file into chroot of microVM. - jailed_mem = vm.create_jailed_resource(mem_path) - # Copy the valid page fault binary into chroot of microVM. - jailed_handler = vm.create_jailed_resource(handler_path) - handler_name = os.path.basename(jailed_handler) - - uffd_handler = UffdHandler( - handler_name, SOCKET_PATH, jailed_mem, vm.chroot(), "uffd.log" - ) - uffd_handler.spawn(vm.jailer.uid, vm.jailer.gid) - - return uffd_handler - - def test_bad_socket_path(uvm_plain, snapshot): """ Test error scenario when socket path does not exist. @@ -100,7 +83,7 @@ def test_unbinded_socket(uvm_plain, snapshot): vm.mark_killed() -def test_valid_handler(uvm_plain, snapshot, uffd_handler_paths): +def test_valid_handler(uvm_plain, snapshot): """ Test valid uffd handler scenario. """ @@ -109,22 +92,24 @@ def test_valid_handler(uvm_plain, snapshot, uffd_handler_paths): vm.spawn() # Spawn page fault handler process. - _pf_handler = spawn_pf_handler( - vm, uffd_handler_paths["valid_handler"], snapshot.mem - ) + spawn_pf_handler(vm, uffd_handler("on_demand"), snapshot) - vm.restore_from_snapshot(snapshot, resume=True, uffd_path=SOCKET_PATH) + vm.restore_from_snapshot(resume=True) # Inflate balloon. vm.api.balloon.patch(amount_mib=200) + # Verify if the restored guest works. + vm.ssh.check_output("true") + # Deflate balloon. vm.api.balloon.patch(amount_mib=0) # Verify if the restored guest works. + vm.ssh.check_output("true") -def test_malicious_handler(uvm_plain, snapshot, uffd_handler_paths): +def test_malicious_handler(uvm_plain, snapshot): """ Test malicious uffd handler scenario. @@ -140,15 +125,13 @@ def test_malicious_handler(uvm_plain, snapshot, uffd_handler_paths): vm.spawn() # Spawn page fault handler process. - _pf_handler = spawn_pf_handler( - vm, uffd_handler_paths["malicious_handler"], snapshot.mem - ) + spawn_pf_handler(vm, uffd_handler("malicious"), snapshot) # We expect Firecracker to freeze while resuming from a snapshot # due to the malicious handler's unavailability. try: with Timeout(seconds=30): - vm.restore_from_snapshot(snapshot, resume=True, uffd_path=SOCKET_PATH) + vm.restore_from_snapshot(resume=True) assert False, "Firecracker should freeze" except (TimeoutError, requests.exceptions.ReadTimeout): pass diff --git a/tests/integration_tests/functional/test_vsock.py b/tests/integration_tests/functional/test_vsock.py index 2d540b8f934..dfa02510b37 100644 --- a/tests/integration_tests/functional/test_vsock.py +++ b/tests/integration_tests/functional/test_vsock.py @@ -199,10 +199,7 @@ def test_vsock_transport_reset_h2g( test_vm.kill() # Load snapshot. - - vm2 = microvm_factory.build() - vm2.spawn() - vm2.restore_from_snapshot(snapshot, resume=True) + vm2 = microvm_factory.build_from_snapshot(snapshot) # Check that vsock device still works. # Test guest-initiated connections. @@ -231,9 +228,7 @@ def test_vsock_transport_reset_g2h(uvm_nano, microvm_factory): for _ in range(5): # Load snapshot. - new_vm = microvm_factory.build() - new_vm.spawn() - new_vm.restore_from_snapshot(snapshot, resume=True) + new_vm = microvm_factory.build_from_snapshot(snapshot) # After snap restore all vsock connections should be # dropped. This means guest socat should exit same way diff --git a/tests/integration_tests/performance/test_drive_rate_limiter.py b/tests/integration_tests/performance/test_drive_rate_limiter.py index adeb0c25123..dbb1bfb310c 100644 --- a/tests/integration_tests/performance/test_drive_rate_limiter.py +++ b/tests/integration_tests/performance/test_drive_rate_limiter.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 """Tests for checking the rate limiter on /drives resources.""" - +import json import os import host_tools.drive as drive_tools @@ -12,28 +12,22 @@ def check_iops_limit(ssh_connection, block_size, count, min_time, max_time): """Verify if the rate limiter throttles block iops using dd.""" - obs = block_size + byte_count = block_size * count - dd = "dd if=/dev/zero of=/dev/vdb ibs={} obs={} count={} oflag=direct".format( - block_size, obs, count - ) - print("Running cmd {}".format(dd)) - # Check write iops (writing with oflag=direct is more reliable). - _, _, stderr = ssh_connection.check_output(dd) - # "dd" writes to stderr by design. We drop first lines - lines = stderr.split("\n") - dd_result = lines[2].strip() + fio = f"fio --name=fixed-job --direct=1 --rw=write --blocksize={block_size} --size={byte_count} --filename=/dev/vdb --zero_buffers --output-format=json" + + _, stdout, _ = ssh_connection.check_output(fio) - # Interesting output looks like this: - # 4194304 bytes (4.2 MB, 4.0 MiB) copied, 0.0528524 s, 79.4 MB/s - tokens = dd_result.split() + data = json.loads(stdout) + runtime_ms = data["jobs"][0]["write"]["runtime"] + io_bytes = data["jobs"][0]["write"]["io_bytes"] # Check total read bytes. - assert int(tokens[0]) == byte_count + assert io_bytes == byte_count # Check duration. - assert float(tokens[7]) > min_time - assert float(tokens[7]) < max_time + assert runtime_ms > min_time * 1000 + assert runtime_ms < max_time * 1000 def test_patch_drive_limiter(uvm_plain): diff --git a/tests/integration_tests/performance/test_huge_pages.py b/tests/integration_tests/performance/test_huge_pages.py index 034ee6749a0..6015cf6032b 100644 --- a/tests/integration_tests/performance/test_huge_pages.py +++ b/tests/integration_tests/performance/test_huge_pages.py @@ -10,7 +10,7 @@ from framework.microvm import HugePagesConfig from framework.properties import global_props from framework.utils_ftrace import ftrace_events -from integration_tests.functional.test_uffd import SOCKET_PATH, spawn_pf_handler +from framework.utils_uffd import spawn_pf_handler, uffd_handler def check_hugetlbfs_in_use(pid: int, allocation_name: str): @@ -69,15 +69,13 @@ def test_hugetlbfs_boot(uvm_plain): ) -def test_hugetlbfs_snapshot( - microvm_factory, guest_kernel_linux_5_10, rootfs_ubuntu_22, uffd_handler_paths -): +def test_hugetlbfs_snapshot(microvm_factory, guest_kernel_linux_5_10, rootfs): """ Test hugetlbfs snapshot restore via uffd """ ### Create Snapshot ### - vm = microvm_factory.build(guest_kernel_linux_5_10, rootfs_ubuntu_22) + vm = microvm_factory.build(guest_kernel_linux_5_10, rootfs) vm.memory_monitor = None vm.spawn() vm.basic_config(huge_pages=HugePagesConfig.HUGETLBFS_2MB, mem_size_mib=128) @@ -95,16 +93,14 @@ def test_hugetlbfs_snapshot( vm.spawn() # Spawn page fault handler process. - _pf_handler = spawn_pf_handler( - vm, uffd_handler_paths["valid_handler"], snapshot.mem - ) + spawn_pf_handler(vm, uffd_handler("on_demand"), snapshot) - vm.restore_from_snapshot(snapshot, resume=True, uffd_path=SOCKET_PATH) + vm.restore_from_snapshot(resume=True) check_hugetlbfs_in_use(vm.firecracker_pid, "/anon_hugepage") -def test_hugetlbfs_diff_snapshot(microvm_factory, uvm_plain, uffd_handler_paths): +def test_hugetlbfs_diff_snapshot(microvm_factory, uvm_plain): """ Test hugetlbfs differential snapshot support. @@ -139,11 +135,9 @@ def test_hugetlbfs_diff_snapshot(microvm_factory, uvm_plain, uffd_handler_paths) vm.spawn() # Spawn page fault handler process. - _pf_handler = spawn_pf_handler( - vm, uffd_handler_paths["valid_handler"], snapshot_merged.mem - ) + spawn_pf_handler(vm, uffd_handler("on_demand"), snapshot_merged) - vm.restore_from_snapshot(snapshot_merged, resume=True, uffd_path=SOCKET_PATH) + vm.restore_from_snapshot(resume=True) # Verify if the restored microvm works. @@ -152,8 +146,7 @@ def test_hugetlbfs_diff_snapshot(microvm_factory, uvm_plain, uffd_handler_paths) def test_ept_violation_count( microvm_factory, guest_kernel_linux_5_10, - rootfs_ubuntu_22, - uffd_handler_paths, + rootfs, metrics, huge_pages, ): @@ -163,7 +156,7 @@ def test_ept_violation_count( """ ### Create Snapshot ### - vm = microvm_factory.build(guest_kernel_linux_5_10, rootfs_ubuntu_22) + vm = microvm_factory.build(guest_kernel_linux_5_10, rootfs) vm.memory_monitor = None vm.spawn() vm.basic_config(huge_pages=huge_pages, mem_size_mib=256) @@ -200,12 +193,10 @@ def test_ept_violation_count( vm.spawn() # Spawn page fault handler process. - _pf_handler = spawn_pf_handler( - vm, uffd_handler_paths["fault_all_handler"], snapshot.mem - ) + spawn_pf_handler(vm, uffd_handler("fault_all"), snapshot) with ftrace_events("kvm:*"): - vm.restore_from_snapshot(snapshot, resume=True, uffd_path=SOCKET_PATH) + vm.restore_from_snapshot(resume=True) # Verify if guest can run commands, and also wake up the fast page fault helper to trigger page faults. vm.ssh.check_output(f"kill -s {signal.SIGUSR1} {pid}") @@ -251,41 +242,3 @@ def test_negative_huge_pages_plus_balloon(uvm_plain): match="Machine config error: Firecracker's huge pages support is incompatible with memory ballooning.", ): uvm_plain.basic_config(huge_pages=HugePagesConfig.HUGETLBFS_2MB) - - -def test_negative_huge_pages_plus_initrd(uvm_with_initrd): - """Tests that huge pages and initrd cannot be used together""" - uvm_with_initrd.jailer.daemonize = False - uvm_with_initrd.spawn() - uvm_with_initrd.memory_monitor = None - - # Ensure setting huge pages and then telling FC to boot an initrd does not work - with pytest.raises( - RuntimeError, - match="Boot source error: Firecracker's huge pages support is incompatible with initrds.", - ): - # `basic_config` first does a PUT to /machine-config, which will apply the huge pages configuration, - # and then a PUT to /boot-source, which will register the initrd - uvm_with_initrd.basic_config( - boot_args="console=ttyS0 reboot=k panic=1 pci=off", - use_initrd=True, - huge_pages=HugePagesConfig.HUGETLBFS_2MB, - add_root_device=False, - vcpu_count=1, - ) - - # Ensure telling FC about the initrd first and then setting huge pages doesn't work - # This first does a PUT to /machine-config to reset the huge pages configuration, before doing a - # PUT to /boot-source to register the initrd - uvm_with_initrd.basic_config( - huge_pages=HugePagesConfig.NONE, - boot_args="console=ttyS0 reboot=k panic=1 pci=off", - use_initrd=True, - ) - with pytest.raises( - RuntimeError, - match="Machine config error: Firecracker's huge pages support is incompatible with initrds.", - ): - uvm_with_initrd.api.machine_config.patch( - huge_pages=HugePagesConfig.HUGETLBFS_2MB - ) diff --git a/tests/integration_tests/functional/test_initrd.py b/tests/integration_tests/performance/test_initrd.py similarity index 56% rename from tests/integration_tests/functional/test_initrd.py rename to tests/integration_tests/performance/test_initrd.py index 3c48ecd63a5..3845e5610c0 100644 --- a/tests/integration_tests/functional/test_initrd.py +++ b/tests/integration_tests/performance/test_initrd.py @@ -1,13 +1,27 @@ # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """Tests for initrd.""" +import pytest -from framework.microvm import Serial +from framework.microvm import HugePagesConfig, Serial INITRD_FILESYSTEM = "rootfs" -def test_microvm_initrd_with_serial(uvm_with_initrd): +@pytest.fixture +def uvm_with_initrd(microvm_factory, guest_kernel, record_property, artifact_dir): + """ + See file:../docs/initrd.md + """ + fs = artifact_dir / "initramfs.cpio" + record_property("rootfs", fs.name) + uvm = microvm_factory.build(guest_kernel) + uvm.initrd_file = fs + yield uvm + + +@pytest.mark.parametrize("huge_pages", HugePagesConfig) +def test_microvm_initrd_with_serial(uvm_with_initrd, huge_pages): """ Test that a boot using initrd successfully loads the root filesystem. """ @@ -21,6 +35,7 @@ def test_microvm_initrd_with_serial(uvm_with_initrd): vcpu_count=1, boot_args="console=ttyS0 reboot=k panic=1 pci=off", use_initrd=True, + huge_pages=huge_pages, ) vm.start() diff --git a/tests/integration_tests/performance/test_network_ab.py b/tests/integration_tests/performance/test_network_ab.py index 60c4f17361e..3a50d864544 100644 --- a/tests/integration_tests/performance/test_network_ab.py +++ b/tests/integration_tests/performance/test_network_ab.py @@ -62,7 +62,7 @@ def test_network_latency(network_microvm, metrics): rounds = 15 request_per_round = 30 - delay = 0.2 + delay = 0.0 metrics.set_dimensions( { diff --git a/tests/integration_tests/performance/test_snapshot_ab.py b/tests/integration_tests/performance/test_snapshot_ab.py index 23224de6b31..b4f1b8f15dc 100644 --- a/tests/integration_tests/performance/test_snapshot_ab.py +++ b/tests/integration_tests/performance/test_snapshot_ab.py @@ -1,17 +1,20 @@ # Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """Performance benchmark for snapshot restore.""" +import re +import signal import tempfile +import time from dataclasses import dataclass from functools import lru_cache -from typing import List import pytest import host_tools.drive as drive_tools -from framework.microvm import Microvm +from framework.microvm import HugePagesConfig, Microvm USEC_IN_MSEC = 1000 +NS_IN_MSEC = 1_000_000 ITERATIONS = 30 @@ -34,21 +37,17 @@ class SnapshotRestoreTest: nets: int = 3 blocks: int = 3 all_devices: bool = False + huge_pages: HugePagesConfig = HugePagesConfig.NONE @property def id(self): """Computes a unique id for this test instance""" return "all_dev" if self.all_devices else f"{self.vcpus}vcpu_{self.mem}mb" - def configure_vm( - self, - microvm_factory, - guest_kernel_acpi, - rootfs, - ) -> Microvm: + def boot_vm(self, microvm_factory, guest_kernel, rootfs) -> Microvm: """Creates the initial snapshot that will be loaded repeatedly to sample latencies""" vm = microvm_factory.build( - guest_kernel_acpi, + guest_kernel, rootfs, monitor_memory=False, ) @@ -58,6 +57,7 @@ def configure_vm( vcpu_count=self.vcpus, mem_size_mib=self.mem, rootfs_io_engine="Sync", + huge_pages=self.huge_pages, ) for _ in range(self.nets): @@ -74,38 +74,9 @@ def configure_vm( ) vm.api.vsock.put(vsock_id="vsock0", guest_cid=3, uds_path="/v.sock") - return vm - - def sample_latency( - self, microvm_factory, snapshot, guest_kernel_linux_5_10 - ) -> List[float]: - """Collects latency samples for the microvm configuration specified by this instance""" - values = [] + vm.start() - for _ in range(ITERATIONS): - microvm = microvm_factory.build( - kernel=guest_kernel_linux_5_10, - monitor_memory=False, - ) - microvm.spawn(emit_metrics=True) - snapshot_copy = microvm.restore_from_snapshot(snapshot, resume=True) - - value = 0 - # Parse all metric data points in search of load_snapshot time. - microvm.flush_metrics() - metrics = microvm.get_all_metrics() - for data_point in metrics: - cur_value = data_point["latencies_us"]["load_snapshot"] - if cur_value > 0: - value = cur_value / USEC_IN_MSEC - break - assert value > 0 - values.append(value) - microvm.kill() - snapshot_copy.delete() - - snapshot.delete() - return values + return vm @pytest.mark.nonci @@ -134,28 +105,147 @@ def test_restore_latency( We only test a single guest kernel, as the guest kernel does not "participate" in snapshot restore. """ - vm = test_setup.configure_vm(microvm_factory, guest_kernel_linux_5_10, rootfs) - vm.start() + vm = test_setup.boot_vm(microvm_factory, guest_kernel_linux_5_10, rootfs) metrics.set_dimensions( { + "net_devices": str(test_setup.nets), + "block_devices": str(test_setup.blocks), + "vsock_devices": str(int(test_setup.all_devices)), + "balloon_devices": str(int(test_setup.all_devices)), + "huge_pages_config": str(test_setup.huge_pages), "performance_test": "test_restore_latency", + "uffd_handler": "None", + **vm.dimensions, + } + ) + + snapshot = vm.snapshot_full() + vm.kill() + for microvm in microvm_factory.build_n_from_snapshot(snapshot, ITERATIONS): + value = 0 + # Parse all metric data points in search of load_snapshot time. + microvm.flush_metrics() + for data_point in microvm.get_all_metrics(): + cur_value = data_point["latencies_us"]["load_snapshot"] + if cur_value > 0: + value = cur_value / USEC_IN_MSEC + break + assert value > 0 + metrics.put_metric("latency", value, "Milliseconds") + + +# When using the fault-all handler, all guest memory will be faulted in way before the helper tool +# wakes up, because it gets faulted in on the first page fault. In this scenario, we are not measuring UFFD +# latencies, but KVM latencies of setting up missing EPT entries. +@pytest.mark.nonci +@pytest.mark.parametrize("uffd_handler", [None, "on_demand", "fault_all"]) +@pytest.mark.parametrize("huge_pages", HugePagesConfig) +def test_post_restore_latency( + microvm_factory, + rootfs, + guest_kernel_linux_5_10, + metrics, + uffd_handler, + huge_pages, +): + """Collects latency metric of post-restore memory accesses done inside the guest""" + if huge_pages != HugePagesConfig.NONE and uffd_handler is None: + pytest.skip("huge page snapshots can only be restored using uffd") + + test_setup = SnapshotRestoreTest(mem=1024, vcpus=2, huge_pages=huge_pages) + vm = test_setup.boot_vm(microvm_factory, guest_kernel_linux_5_10, rootfs) + + metrics.set_dimensions( + { "net_devices": str(test_setup.nets), "block_devices": str(test_setup.blocks), "vsock_devices": str(int(test_setup.all_devices)), "balloon_devices": str(int(test_setup.all_devices)), + "huge_pages_config": str(test_setup.huge_pages), + "performance_test": "test_post_restore_latency", + "uffd_handler": str(uffd_handler), **vm.dimensions, } ) + vm.ssh.check_output( + "nohup /usr/local/bin/fast_page_fault_helper >/dev/null 2>&1 high->low->high and so the numbers are not in monotonic sequence. new_sizes = [20, 10, 30] # MB - vm = microvm_factory.build( - guest_kernel_acpi, rootfs_ubuntu_22, monitor_memory=False - ) + vm = microvm_factory.build(guest_kernel_acpi, rootfs, monitor_memory=False) vm.spawn(log_level="Info") vm.basic_config(vcpu_count=vcpu_count) vm.add_net_iface() diff --git a/tests/integration_tests/security/conftest.py b/tests/integration_tests/security/conftest.py new file mode 100644 index 00000000000..1cce3067b2f --- /dev/null +++ b/tests/integration_tests/security/conftest.py @@ -0,0 +1,29 @@ +# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Fixtures for security tests""" + +import json +from pathlib import Path + +import pytest + +from host_tools.cargo_build import run_seccompiler_bin + + +@pytest.fixture() +def seccompiler(tmp_path): + "A seccompiler helper fixture" + + class Seccompiler: + "A seccompiler helper class" + + def compile(self, data: dict, basic=False) -> Path: + "Use seccompiler-bin to compile a filter from a dict" + inp = tmp_path / "input.json" + inp.write_text(json.dumps(data)) + bpf = tmp_path / "output.bpfmap" + run_seccompiler_bin(bpf_path=bpf, json_path=inp, basic=basic) + return bpf + + return Seccompiler() diff --git a/tests/integration_tests/security/test_custom_seccomp.py b/tests/integration_tests/security/test_custom_seccomp.py index 5fffb83f6cc..05f9b9aa96e 100644 --- a/tests/integration_tests/security/test_custom_seccomp.py +++ b/tests/integration_tests/security/test_custom_seccomp.py @@ -2,179 +2,76 @@ # SPDX-License-Identifier: Apache-2.0 """Tests that the --seccomp-filter parameter works as expected.""" -import os import platform -import tempfile import time +from pathlib import Path -import pytest import requests from framework import utils -from host_tools.cargo_build import run_seccompiler_bin -def _custom_filter_setup(test_microvm, json_filter): - json_temp = tempfile.NamedTemporaryFile(delete=False) - json_temp.write(json_filter) - json_temp.flush() +def install_filter(microvm, bpf_path): + """Install seccomp filter in microvm.""" + microvm.create_jailed_resource(bpf_path) + microvm.jailer.extra_args.update({"seccomp-filter": bpf_path.name}) - bpf_path = os.path.join(test_microvm.path, "bpf.out") - run_seccompiler_bin(bpf_path=bpf_path, json_path=json_temp.name) +def test_allow_all(uvm_plain, seccompiler): + """Test --seccomp-filter, allowing all syscalls.""" + seccomp_filter = { + thread: {"default_action": "allow", "filter_action": "trap", "filter": []} + for thread in ["vmm", "api", "vcpu"] + } - os.unlink(json_temp.name) - test_microvm.create_jailed_resource(bpf_path) - test_microvm.jailer.extra_args.update({"seccomp-filter": "bpf.out"}) - - -def _config_file_setup(test_microvm, vm_config_file): - test_microvm.create_jailed_resource(test_microvm.kernel_file) - test_microvm.create_jailed_resource(test_microvm.rootfs_file) - - vm_config_path = os.path.join(test_microvm.path, os.path.basename(vm_config_file)) - with open(vm_config_file, encoding="utf-8") as f1: - with open(vm_config_path, "w", encoding="utf-8") as f2: - for line in f1: - f2.write(line) - test_microvm.create_jailed_resource(vm_config_path) - test_microvm.jailer.extra_args = {"config-file": os.path.basename(vm_config_file)} - - test_microvm.jailer.extra_args.update({"no-api": None}) - - -def test_allow_all(uvm_plain): - """ - Test --seccomp-filter, allowing all syscalls. - """ + bpf_path = seccompiler.compile(seccomp_filter) test_microvm = uvm_plain - - _custom_filter_setup( - test_microvm, - """{ - "Vmm": { - "default_action": "allow", - "filter_action": "trap", - "filter": [] - }, - "Api": { - "default_action": "allow", - "filter_action": "trap", - "filter": [] - }, - "Vcpu": { - "default_action": "allow", - "filter_action": "trap", - "filter": [] - } - }""".encode( - "utf-8" - ), - ) - + install_filter(test_microvm, bpf_path) test_microvm.spawn() - test_microvm.basic_config() - test_microvm.start() - utils.assert_seccomp_level(test_microvm.firecracker_pid, "2") -def test_working_filter(uvm_plain): - """ - Test --seccomp-filter, rejecting some dangerous syscalls. - """ - test_microvm = uvm_plain +def test_working_filter(uvm_plain, seccompiler): + """Test --seccomp-filter, rejecting some dangerous syscalls.""" - _custom_filter_setup( - test_microvm, - """{ - "Vmm": { - "default_action": "allow", - "filter_action": "kill_process", - "filter": [ - { - "syscall": "clone" - }, - { - "syscall": "execve" - } - ] - }, - "Api": { - "default_action": "allow", - "filter_action": "kill_process", - "filter": [ - { - "syscall": "clone" - }, - { - "syscall": "execve" - } - ] - }, - "Vcpu": { + seccomp_filter = { + thread: { "default_action": "allow", "filter_action": "kill_process", - "filter": [ - { - "syscall": "clone" - }, - { - "syscall": "execve", - "comment": "sample comment" - } - ] + "filter": [{"syscall": "clone"}, {"syscall": "execve"}], } - }""".encode( - "utf-8" - ), - ) + for thread in ["vmm", "api", "vcpu"] + } + bpf_path = seccompiler.compile(seccomp_filter) + test_microvm = uvm_plain + install_filter(test_microvm, bpf_path) test_microvm.spawn() - test_microvm.basic_config() - test_microvm.start() # level should be 2, with no additional errors utils.assert_seccomp_level(test_microvm.firecracker_pid, "2") -def test_failing_filter(uvm_plain): - """ - Test --seccomp-filter, denying some needed syscalls. - """ - test_microvm = uvm_plain +def test_failing_filter(uvm_plain, seccompiler): + """Test --seccomp-filter, denying some needed syscalls.""" - _custom_filter_setup( - test_microvm, - """{ - "Vmm": { + seccomp_filter = { + "vmm": {"default_action": "allow", "filter_action": "trap", "filter": []}, + "api": {"default_action": "allow", "filter_action": "trap", "filter": []}, + "vcpu": { "default_action": "allow", "filter_action": "trap", - "filter": [] + "filter": [{"syscall": "ioctl"}], }, - "Api": { - "default_action": "allow", - "filter_action": "trap", - "filter": [] - }, - "Vcpu": { - "default_action": "allow", - "filter_action": "trap", - "filter": [ - { - "syscall": "ioctl" - } - ] - } - }""".encode( - "utf-8" - ), - ) + } + bpf_path = seccompiler.compile(seccomp_filter) + test_microvm = uvm_plain + install_filter(test_microvm, bpf_path) test_microvm.spawn() test_microvm.basic_config(vcpu_count=1) @@ -190,8 +87,7 @@ def test_failing_filter(uvm_plain): # Check the logger output ioctl_num = 16 if platform.machine() == "x86_64" else 29 test_microvm.check_log_message( - "Shutting down VM after intercepting a bad" - " syscall ({})".format(str(ioctl_num)) + f"Shutting down VM after intercepting a bad syscall ({ioctl_num})" ) # Check the metrics @@ -208,28 +104,28 @@ def test_failing_filter(uvm_plain): test_microvm.mark_killed() -@pytest.mark.parametrize("vm_config_file", ["framework/vm_config.json"]) -def test_invalid_bpf(uvm_plain, vm_config_file): - """ - Test that FC does not start, given an invalid binary filter. - """ +def test_invalid_bpf(uvm_plain): + """Test that FC does not start, given an invalid binary filter.""" test_microvm = uvm_plain # Configure VM from JSON. Otherwise, the test will error because # the process will be killed before configuring the API socket. - _config_file_setup(uvm_plain, vm_config_file) + test_microvm.create_jailed_resource(test_microvm.kernel_file) + test_microvm.create_jailed_resource(test_microvm.rootfs_file) - bpf_path = os.path.join(test_microvm.path, "bpf.out") - file = open(bpf_path, "w", encoding="utf-8") - file.write("Invalid BPF!") - file.close() + vm_config_file = Path("framework/vm_config.json") + test_microvm.create_jailed_resource(vm_config_file) + test_microvm.jailer.extra_args = {"config-file": vm_config_file.name} + test_microvm.jailer.extra_args.update({"no-api": None}) + bpf_path = Path(test_microvm.path) / "bpf.out" + bpf_path.write_bytes(b"Invalid BPF!") test_microvm.create_jailed_resource(bpf_path) - test_microvm.jailer.extra_args.update({"seccomp-filter": "bpf.out"}) + test_microvm.jailer.extra_args.update({"seccomp-filter": bpf_path.name}) test_microvm.spawn() - # give time for the process to get killed time.sleep(1) + assert "Seccomp error: Filter deserialization failed" in test_microvm.log_data test_microvm.mark_killed() diff --git a/tests/integration_tests/security/test_nv.py b/tests/integration_tests/security/test_nv.py index ce848c6a789..73f042d5ae3 100644 --- a/tests/integration_tests/security/test_nv.py +++ b/tests/integration_tests/security/test_nv.py @@ -16,33 +16,7 @@ start providing the feature by mistake. """ -import pytest - -@pytest.fixture -def uvm_with_cpu_template( - microvm_factory, guest_kernel, rootfs_ubuntu_22, cpu_template_any -): - """A microvm fixture parametrized with all possible templates""" - vm = microvm_factory.build(guest_kernel, rootfs_ubuntu_22) - vm.spawn() - cpu_template = None - if isinstance(cpu_template_any, str): - cpu_template = cpu_template_any - vm.basic_config(cpu_template=cpu_template) - if cpu_template is None: - vm.api.cpu_config.put(**cpu_template_any["template"]) - vm.add_net_iface() - vm.start() - yield vm - - -def test_no_nv_when_using_cpu_templates(uvm_with_cpu_template): - """ - Double-check that guests using CPU templates don't have Nested Virtualization - enabled. - """ - - vm = uvm_with_cpu_template - rc, _, _ = vm.ssh.run("[ ! -e /dev/kvm ]") - assert rc == 0, "/dev/kvm exists" +def test_no_nested_virtualization(uvm_any_booted): + """Validate that guests don't have Nested Virtualization enabled.""" + uvm_any_booted.ssh.check_output("[ ! -e /dev/kvm ]") diff --git a/tests/integration_tests/security/test_sec_audit.py b/tests/integration_tests/security/test_sec_audit.py index 1ad625cc7c9..e8265c3ae2a 100644 --- a/tests/integration_tests/security/test_sec_audit.py +++ b/tests/integration_tests/security/test_sec_audit.py @@ -35,6 +35,6 @@ def set_of_vulnerabilities(output: CommandReturn): ) git_ab_test_host_command_if_pr( - "cargo audit --deny warnings -q --json |grep -Po '{.*}'", + "cargo audit --deny warnings -q --json", comparator=set_did_not_grow_comparator(set_of_vulnerabilities), ) diff --git a/tests/integration_tests/security/test_seccomp.py b/tests/integration_tests/security/test_seccomp.py index 0fa9aefafcb..8ae87271daf 100644 --- a/tests/integration_tests/security/test_seccomp.py +++ b/tests/integration_tests/security/test_seccomp.py @@ -2,91 +2,47 @@ # SPDX-License-Identifier: Apache-2.0 """Tests that the seccomp filters don't let denied syscalls through.""" -import json as json_lib +import json import os import platform -import tempfile +from pathlib import Path from framework import utils -from host_tools.cargo_build import run_seccompiler_bin + +ARCH = platform.machine() def _get_basic_syscall_list(): """Return the JSON list of syscalls that the demo jailer needs.""" - if platform.machine() == "x86_64": - sys_list = [ - "rt_sigprocmask", - "rt_sigaction", - "execve", - "mmap", - "mprotect", + sys_list = [ + "rt_sigprocmask", + "rt_sigaction", + "execve", + "mmap", + "mprotect", + "set_tid_address", + "read", + "close", + "brk", + "sched_getaffinity", + "sigaltstack", + "munmap", + "exit_group", + ] + if ARCH == "x86_64": + sys_list += [ "arch_prctl", - "set_tid_address", "readlink", "open", - "read", - "close", - "brk", - "sched_getaffinity", - "sigaltstack", - "munmap", - "exit_group", "poll", ] - else: - # platform.machine() == "aarch64" - sys_list = [ - "rt_sigprocmask", - "rt_sigaction", - "execve", - "mmap", - "mprotect", - "set_tid_address", - "read", - "close", - "brk", - "sched_getaffinity", - "sigaltstack", - "munmap", - "exit_group", - "ppoll", - ] - - json = "" - for syscall in sys_list[0:-1]: - json += """ - {{ - "syscall": \"{}\" - }}, - """.format( - syscall - ) - - json += """ - {{ - "syscall": \"{}\" - }} - """.format( - sys_list[-1] - ) - - return json - - -def _run_seccompiler_bin(json_data, basic=False): - json_temp = tempfile.NamedTemporaryFile(delete=False) - json_temp.write(json_data.encode("utf-8")) - json_temp.flush() - - bpf_temp = tempfile.NamedTemporaryFile(delete=False) + elif ARCH == "aarch64": + sys_list += ["ppoll"] - run_seccompiler_bin(bpf_path=bpf_temp.name, json_path=json_temp.name, basic=basic) + return sys_list - os.unlink(json_temp.name) - return bpf_temp.name - -def test_seccomp_ls(bin_seccomp_paths): +def test_seccomp_ls(bin_seccomp_paths, seccompiler): """ Assert that the seccomp filter denies an unallowed syscall. """ @@ -99,32 +55,26 @@ def test_seccomp_ls(bin_seccomp_paths): demo_jailer = bin_seccomp_paths["demo_jailer"] assert os.path.exists(demo_jailer) - json_filter = """{{ - "main": {{ + json_filter = { + "main": { "default_action": "trap", "filter_action": "allow", - "filter": [ - {} - ] - }} - }}""".format( - _get_basic_syscall_list() - ) + "filter": [{"syscall": x} for x in _get_basic_syscall_list()], + } + } # Run seccompiler-bin. - bpf_path = _run_seccompiler_bin(json_filter) + bpf_path = seccompiler.compile(json_filter) # Run the mini jailer. outcome = utils.run_cmd([demo_jailer, ls_command_path, bpf_path], shell=False) - os.unlink(bpf_path) - # The seccomp filters should send SIGSYS (31) to the binary. `ls` doesn't # handle it, so it will exit with error. assert outcome.returncode != 0 -def test_advanced_seccomp(bin_seccomp_paths): +def test_advanced_seccomp(bin_seccomp_paths, seccompiler): """ Test seccompiler-bin with `demo_jailer`. @@ -143,39 +93,37 @@ def test_advanced_seccomp(bin_seccomp_paths): assert os.path.exists(demo_harmless) assert os.path.exists(demo_malicious) - json_filter = """{{ - "main": {{ + json_filter = { + "main": { "default_action": "trap", "filter_action": "allow", "filter": [ - {}, - {{ + *[{"syscall": x} for x in _get_basic_syscall_list()], + { "syscall": "write", "args": [ - {{ + { "index": 0, "type": "dword", "op": "eq", "val": 1, - "comment": "stdout fd" - }}, - {{ + "comment": "stdout fd", + }, + { "index": 2, "type": "qword", "op": "eq", "val": 14, - "comment": "nr of bytes" - }} - ] - }} - ] - }} - }}""".format( - _get_basic_syscall_list() - ) + "comment": "nr of bytes", + }, + ], + }, + ], + } + } # Run seccompiler-bin. - bpf_path = _run_seccompiler_bin(json_filter) + bpf_path = seccompiler.compile(json_filter) # Run the mini jailer for harmless binary. outcome = utils.run_cmd([demo_jailer, demo_harmless, bpf_path], shell=False) @@ -189,10 +137,8 @@ def test_advanced_seccomp(bin_seccomp_paths): # The demo malicious binary should have received `SIGSYS`. assert outcome.returncode == -31 - os.unlink(bpf_path) - # Run seccompiler-bin with `--basic` flag. - bpf_path = _run_seccompiler_bin(json_filter, basic=True) + bpf_path = seccompiler.compile(json_filter, basic=True) # Run the mini jailer for malicious binary. outcome = utils.run_cmd([demo_jailer, demo_malicious, bpf_path], shell=False) @@ -201,28 +147,20 @@ def test_advanced_seccomp(bin_seccomp_paths): # disables all argument checks. assert outcome.returncode == 0 - os.unlink(bpf_path) - # Run the mini jailer with an empty allowlist. It should trap on any # syscall. - json_filter = """{ - "main": { - "default_action": "trap", - "filter_action": "allow", - "filter": [] - } - }""" + json_filter = { + "main": {"default_action": "trap", "filter_action": "allow", "filter": []} + } # Run seccompiler-bin. - bpf_path = _run_seccompiler_bin(json_filter) + bpf_path = seccompiler.compile(json_filter) outcome = utils.run_cmd([demo_jailer, demo_harmless, bpf_path], shell=False) # The demo binary should have received `SIGSYS`. assert outcome.returncode == -31 - os.unlink(bpf_path) - def test_no_seccomp(uvm_plain): """ @@ -231,11 +169,8 @@ def test_no_seccomp(uvm_plain): test_microvm = uvm_plain test_microvm.jailer.extra_args.update({"no-seccomp": None}) test_microvm.spawn() - test_microvm.basic_config() - test_microvm.start() - utils.assert_seccomp_level(test_microvm.firecracker_pid, "0") @@ -245,15 +180,12 @@ def test_default_seccomp_level(uvm_plain): """ test_microvm = uvm_plain test_microvm.spawn() - test_microvm.basic_config() - test_microvm.start() - utils.assert_seccomp_level(test_microvm.firecracker_pid, "2") -def test_seccomp_rust_panic(bin_seccomp_paths): +def test_seccomp_rust_panic(bin_seccomp_paths, seccompiler): """ Test seccompiler-bin with `demo_panic`. @@ -266,19 +198,15 @@ def test_seccomp_rust_panic(bin_seccomp_paths): demo_panic = bin_seccomp_paths["demo_panic"] assert os.path.exists(demo_panic) - fc_filters_path = "../resources/seccomp/{}-unknown-linux-musl.json".format( - platform.machine() - ) - with open(fc_filters_path, "r", encoding="utf-8") as fc_filters: - filter_threads = list(json_lib.loads(fc_filters.read())) + fc_filters = Path(f"../resources/seccomp/{ARCH}-unknown-linux-musl.json") + fc_filters_data = json.loads(fc_filters.read_text(encoding="ascii")) + filter_threads = list(fc_filters_data) - bpf_temp = tempfile.NamedTemporaryFile(delete=False) - run_seccompiler_bin(bpf_path=bpf_temp.name, json_path=fc_filters_path) - bpf_path = bpf_temp.name + bpf_path = seccompiler.compile(fc_filters_data) # Run the panic binary with all filters. for thread in filter_threads: - code, _, _ = utils.run_cmd([demo_panic, bpf_path, thread], shell=False) + code, _, _ = utils.run_cmd([demo_panic, str(bpf_path), thread], shell=False) # The demo panic binary should have terminated with SIGABRT # and not with a seccomp violation. # On a seccomp violation, the program exits with code -31 for @@ -286,8 +214,4 @@ def test_seccomp_rust_panic(bin_seccomp_paths): # is for SIGABRT. assert ( code == -6 - ), "Panic binary failed with exit code {} on {} " "filters.".format( - code, thread - ) - - os.unlink(bpf_path) + ), f"Panic binary failed with exit code {code} on {thread} filters." diff --git a/tests/integration_tests/security/test_seccomp_validate.py b/tests/integration_tests/security/test_seccomp_validate.py new file mode 100644 index 00000000000..b91c7590a2b --- /dev/null +++ b/tests/integration_tests/security/test_seccomp_validate.py @@ -0,0 +1,138 @@ +# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Test that validates that seccompiler filters work as expected""" + +import json +import platform +import resource +import struct +from pathlib import Path + +import pytest +import seccomp + +from framework import utils + +ARCH = platform.machine() + + +@pytest.fixture +def bin_test_syscall(tmp_path): + """Build the test_syscall binary.""" + test_syscall_bin = tmp_path / "test_syscall" + compile_cmd = f"musl-gcc -static host_tools/test_syscalls.c -o {test_syscall_bin}" + utils.check_output(compile_cmd) + assert test_syscall_bin.exists() + yield test_syscall_bin.resolve() + + +class BpfMapReader: + """ + Simple reader for the files that seccompiler-bin produces + + The files are serialized with bincode[1] in format that is easy to parse. + + sock_filter = + BpfProgram = Vec + BpfMap = BTreeMap(str, BpfProgram) + str = Vec + + [1] https://github.com/bincode-org/bincode/blob/trunk/docs/spec.md + """ + + INSN_FMT = " set: + """Parses the output of `spectre-meltdown-checker.sh --batch json` + and returns the set of issues for which it reported 'Vulnerable'. - return download_checker + Sample stdout: + ``` + [ + { + "NAME": "SPECTRE VARIANT 1", + "CVE": "CVE-2017-5753", + "VULNERABLE": false, + "INFOS": "Mitigation: usercopy/swapgs barriers and __user pointer sanitization" + }, + { ... } + ] + ``` + """ + vm.ssh.scp_put(self.path, REMOTE_CHECKER_PATH) + res = vm.ssh.run(REMOTE_CHECKER_COMMAND) + return self._parse_output(res.stdout) + + def get_report_for_host(self) -> set: + """Runs `spectre-meltdown-checker.sh` in the host and returns the set of + issues for which it reported 'Vulnerable'. + """ + + res = utils.check_output(f"sh {self.path} --batch json") + return self._parse_output(res.stdout) + + def expected_vulnerabilities(self, cpu_template_name): + """ + There is a REPTAR exception reported on INTEL_ICELAKE when spectre-meltdown-checker.sh + script is run inside the guest from below the tests: + test_spectre_meltdown_checker_on_guest and + test_spectre_meltdown_checker_on_restored_guest + The same script when run on host doesn't report the + exception which means the instances are actually not vulnerable to REPTAR. + The only reason why the script cannot determine if the guest + is vulnerable or not because Firecracker does not expose the microcode + version to the guest. + + The check in spectre_meltdown_checker is here: + https://github.com/speed47/spectre-meltdown-checker/blob/0f2edb1a71733c1074550166c5e53abcfaa4d6ca/spectre-meltdown-checker.sh#L6635-L6637 + + Since we have a test on host and the exception in guest is not valid, + we add a check to ignore this exception. + """ + if ( + global_props.cpu_codename in ["INTEL_ICELAKE", "INTEL_SAPPHIRE_RAPIDS"] + and cpu_template_name is None + ): + return { + '{"NAME": "REPTAR", "CVE": "CVE-2023-23583", "VULNERABLE": true, "INFOS": "Your microcode is too old to mitigate the vulnerability"}' + } + return set() @pytest.fixture(scope="session", name="spectre_meltdown_checker") @@ -149,189 +100,32 @@ def download_spectre_meltdown_checker(tmp_path_factory): """Download spectre / meltdown checker script.""" resp = requests.get(CHECKER_URL, timeout=5) resp.raise_for_status() - path = tmp_path_factory.mktemp("tmp", True) / CHECKER_FILENAME path.write_bytes(resp.content) - - return path - - -def spectre_meltdown_reported_vulnerablities( - spectre_meltdown_checker_output: CommandReturn, -) -> set: - """ - Parses the output of `spectre-meltdown-checker.sh --batch json` and returns the set of issues - for which it reported 'Vulnerable'. - - Sample stdout: - ``` - [ - { - "NAME": "SPECTRE VARIANT 1", - "CVE": "CVE-2017-5753", - "VULNERABLE": false, - "INFOS": "Mitigation: usercopy/swapgs barriers and __user pointer sanitization" - }, - { - ... - } - ] - ``` - """ - return { - json.dumps(entry) # dict is unhashable - for entry in json.loads(spectre_meltdown_checker_output.stdout) - if entry["VULNERABLE"] - } - - -def check_vulnerabilities_on_guest(status): - """ - There is a REPTAR exception reported on INTEL_ICELAKE when spectre-meltdown-checker.sh - script is run inside the guest from below the tests: - test_spectre_meltdown_checker_on_guest and - test_spectre_meltdown_checker_on_restored_guest - The same script when run on host doesn't report the - exception which means the instances are actually not vulnerable to REPTAR. - The only reason why the script cannot determine if the guest - is vulnerable or not because Firecracker does not expose the microcode - version to the guest. - - The check in spectre_meltdown_checker is here: - https://github.com/speed47/spectre-meltdown-checker/blob/0f2edb1a71733c1074550166c5e53abcfaa4d6ca/spectre-meltdown-checker.sh#L6635-L6637 - - Since we have a test on host and the exception in guest is not valid, - we add a check to ignore this exception. - """ - report_guest_vulnerabilities = spectre_meltdown_reported_vulnerablities(status) - known_guest_vulnerabilities = set() - if global_props.cpu_codename == "INTEL_ICELAKE": - known_guest_vulnerabilities = { - '{"NAME": "REPTAR", "CVE": "CVE-2023-23583", "VULNERABLE": true, "INFOS": "Your microcode is too old to mitigate the vulnerability"}' - } - assert report_guest_vulnerabilities == known_guest_vulnerabilities + return SpectreMeltdownChecker(path) # Nothing can be sensibly tested in a PR context here @pytest.mark.skipif( - is_pr(), reason="Test depends solely on factors external to GitHub repository" + global_props.buildkite_pr, + reason="Test depends solely on factors external to GitHub repository", ) def test_spectre_meltdown_checker_on_host(spectre_meltdown_checker): - """ - Test with the spectre / meltdown checker on host. - """ - rc, output, _ = utils.run_cmd(f"sh {spectre_meltdown_checker} --batch json") - - if output and rc != 0: - report = spectre_meltdown_reported_vulnerablities(output) - expected = {} - assert report == expected, f"Unexpected vulnerabilities: {report} vs {expected}" - - -def test_spectre_meltdown_checker_on_guest(spectre_meltdown_checker, build_microvm): - """ - Test with the spectre / meltdown checker on guest. - """ - - status = precompiled_ab_test_guest_command_if_pr( - with_checker(build_microvm, spectre_meltdown_checker), - REMOTE_CHECKER_COMMAND, - comparator=set_did_not_grow_comparator( - spectre_meltdown_reported_vulnerablities - ), - check_in_nonpr=False, - ) - if status and status.returncode != 0: - check_vulnerabilities_on_guest(status) - - -def test_spectre_meltdown_checker_on_restored_guest( - spectre_meltdown_checker, build_microvm, microvm_factory -): - """ - Test with the spectre / meltdown checker on a restored guest. - """ - status = precompiled_ab_test_guest_command_if_pr( - with_checker( - with_restore(build_microvm, microvm_factory), spectre_meltdown_checker - ), - REMOTE_CHECKER_COMMAND, - comparator=set_did_not_grow_comparator( - spectre_meltdown_reported_vulnerablities - ), - check_in_nonpr=False, - ) - if status and status.returncode != 0: - check_vulnerabilities_on_guest(status) - - -def test_spectre_meltdown_checker_on_guest_with_template( - spectre_meltdown_checker, build_microvm_with_template -): - """ - Test with the spectre / meltdown checker on guest with CPU template. - """ + """Test with the spectre / meltdown checker on host.""" + report = spectre_meltdown_checker.get_report_for_host() + assert report == set(), f"Unexpected vulnerabilities: {report}" - precompiled_ab_test_guest_command_if_pr( - with_checker(build_microvm_with_template, spectre_meltdown_checker), - REMOTE_CHECKER_COMMAND, - comparator=set_did_not_grow_comparator( - spectre_meltdown_reported_vulnerablities - ), - ) - -def test_spectre_meltdown_checker_on_guest_with_custom_template( - spectre_meltdown_checker, build_microvm_with_custom_template -): - """ - Test with the spectre / meltdown checker on guest with a custom CPU template. - """ - precompiled_ab_test_guest_command_if_pr( - with_checker(build_microvm_with_custom_template, spectre_meltdown_checker), - REMOTE_CHECKER_COMMAND, - comparator=set_did_not_grow_comparator( - spectre_meltdown_reported_vulnerablities - ), - ) - - -def test_spectre_meltdown_checker_on_restored_guest_with_template( - spectre_meltdown_checker, build_microvm_with_template, microvm_factory -): - """ - Test with the spectre / meltdown checker on a restored guest with a CPU template. - """ - precompiled_ab_test_guest_command_if_pr( - with_checker( - with_restore(build_microvm_with_template, microvm_factory), - spectre_meltdown_checker, - ), - REMOTE_CHECKER_COMMAND, - comparator=set_did_not_grow_comparator( - spectre_meltdown_reported_vulnerablities - ), - ) - - -def test_spectre_meltdown_checker_on_restored_guest_with_custom_template( - spectre_meltdown_checker, - build_microvm_with_custom_template, - microvm_factory, -): - """ - Test with the spectre / meltdown checker on a restored guest with a custom CPU template. - """ - precompiled_ab_test_guest_command_if_pr( - with_checker( - with_restore(build_microvm_with_custom_template, microvm_factory), - spectre_meltdown_checker, - ), - REMOTE_CHECKER_COMMAND, - comparator=set_did_not_grow_comparator( - spectre_meltdown_reported_vulnerablities - ), - ) +# Nothing can be sensibly tested here in a PR context +@pytest.mark.skipif( + global_props.buildkite_pr, + reason="Test depends solely on factors external to GitHub repository", +) +def test_vulnerabilities_on_host(): + """Test vulnerability files on host.""" + res = utils.run_cmd(f"grep -r Vulnerable {VULN_DIR}") + # if grep finds no matching lines, it exits with status 1 + assert res.returncode == 1, res.stdout def get_vuln_files_exception_dict(template): @@ -372,25 +166,14 @@ def get_vuln_files_exception_dict(template): # Since those bits are not set on Intel Skylake and C3 template makes guests pretend to be AWS # C3 instance (quite old processor now) by overwriting CPUID.1H:EAX, it is impossible to avoid # this "Unknown" state. - if global_props.cpu_codename == "INTEL_SKYLAKE" and template == "C3": + if global_props.cpu_codename == "INTEL_SKYLAKE" and template == "c3": exception_dict["mmio_stale_data"] = "Unknown: No mitigations" - elif global_props.cpu_codename == "INTEL_SKYLAKE" or template == "T2S": + elif global_props.cpu_codename == "INTEL_SKYLAKE" or template == "t2s": exception_dict["mmio_stale_data"] = "Clear CPU buffers" return exception_dict -# Nothing can be sensibly tested here in a PR context -@pytest.mark.skipif( - is_pr(), reason="Test depends solely on factors external to GitHub repository" -) -def test_vulnerabilities_on_host(): - """ - Test vulnerabilities files on host. - """ - utils.check_output(f"! grep -r Vulnerable {VULN_DIR}") - - def check_vulnerabilities_files_on_guest(microvm): """ Check that the guest's vulnerabilities files do not contain `Vulnerable`. @@ -400,88 +183,73 @@ def check_vulnerabilities_files_on_guest(microvm): # Retrieve a list of vulnerabilities files available inside guests. vuln_dir = "/sys/devices/system/cpu/vulnerabilities" _, stdout, _ = microvm.ssh.check_output(f"find -D all {vuln_dir} -type f") - vuln_files = stdout.split("\n") + vuln_files = stdout.splitlines() # Fixtures in this file (test_vulnerabilities.py) add this special field. - template = microvm.cpu_template + template = microvm.cpu_template_name # Check that vulnerabilities files in the exception dictionary have the expected values and # the others do not contain "Vulnerable". exceptions = get_vuln_files_exception_dict(template) + results = [] for vuln_file in vuln_files: - filename = os.path.basename(vuln_file) + filename = Path(vuln_file).name if filename in exceptions: - _, stdout, _ = microvm.ssh.run(f"cat {vuln_file}") + _, stdout, _ = microvm.ssh.check_output(f"cat {vuln_file}") assert exceptions[filename] in stdout else: cmd = f"grep Vulnerable {vuln_file}" - ecode, stdout, stderr = microvm.ssh.run(cmd) - assert ecode == 1, f"{vuln_file}: stdout:\n{stdout}\nstderr:\n{stderr}\n" - - -def check_vulnerabilities_files_ab(builder): - """Does an A/B test on the contents of the /sys/devices/system/cpu/vulnerabilities files in the guest if - running in a PR pipeline, and otherwise calls `check_vulnerabilities_files_on_guest` - """ - if is_pr(): - precompiled_ab_test_guest_command( - builder, - f"! grep -r Vulnerable {VULN_DIR}", - comparator=set_did_not_grow_comparator( - lambda output: set(output.stdout.splitlines()) - ), - ) - else: - check_vulnerabilities_files_on_guest(builder()) + _ecode, stdout, _stderr = microvm.ssh.run(cmd) + results.append({"file": vuln_file, "stdout": stdout}) + return results -def test_vulnerabilities_files_on_guest(build_microvm): - """ - Test vulnerabilities files on guest. - """ - check_vulnerabilities_files_ab(build_microvm) - - -def test_vulnerabilities_files_on_restored_guest(build_microvm, microvm_factory): - """ - Test vulnerabilities files on a restored guest. - """ - check_vulnerabilities_files_ab(with_restore(build_microvm, microvm_factory)) +@pytest.fixture +def microvm_factory_a(record_property): + """MicroVMFactory using revision A binaries""" + revision_a = global_props.buildkite_revision_a + bin_dir = git_clone(Path("../build") / revision_a, revision_a).resolve() + record_property("firecracker_bin", str(bin_dir / "firecracker")) + uvm_factory = MicroVMFactory(bin_dir) + yield uvm_factory + uvm_factory.kill() -def test_vulnerabilities_files_on_guest_with_template(build_microvm_with_template): - """ - Test vulnerabilities files on guest with CPU template. - """ - check_vulnerabilities_files_ab(build_microvm_with_template) - +@pytest.fixture +def uvm_any_a(microvm_factory_a, uvm_ctor, guest_kernel, rootfs, cpu_template_any): + """Return uvm with revision A firecracker -def test_vulnerabilities_files_on_guest_with_custom_template( - build_microvm_with_custom_template, -): + Since pytest caches fixtures, this guarantees uvm_any_a will match a vm from uvm_any. + See https://docs.pytest.org/en/stable/how-to/fixtures.html#fixtures-can-be-requested-more-than-once-per-test-return-values-are-cached """ - Test vulnerabilities files on guest with a custom CPU template. - """ - check_vulnerabilities_files_ab(build_microvm_with_custom_template) + return uvm_ctor(microvm_factory_a, guest_kernel, rootfs, cpu_template_any) -def test_vulnerabilities_files_on_restored_guest_with_template( - build_microvm_with_template, microvm_factory -): - """ - Test vulnerabilities files on a restored guest with a CPU template. - """ - check_vulnerabilities_files_ab( - with_restore(build_microvm_with_template, microvm_factory) - ) +def test_check_vulnerability_files_ab(request, uvm_any): + """Test vulnerability files on guests""" + res_b = check_vulnerabilities_files_on_guest(uvm_any) + if global_props.buildkite_pr: + # we only get the uvm_any_a fixtures if we need it + uvm_a = request.getfixturevalue("uvm_any_a") + res_a = check_vulnerabilities_files_on_guest(uvm_a) + assert res_b <= res_a + else: + assert not [x for x in res_b if "Vulnerable" in x["stdout"]] -def test_vulnerabilities_files_on_restored_guest_with_custom_template( - build_microvm_with_custom_template, microvm_factory +def test_spectre_meltdown_checker_on_guest( + request, + uvm_any, + spectre_meltdown_checker, ): - """ - Test vulnerabilities files on a restored guest with a custom CPU template. - """ - check_vulnerabilities_files_ab( - with_restore(build_microvm_with_custom_template, microvm_factory) - ) + """Test with the spectre / meltdown checker on any supported guest.""" + res_b = spectre_meltdown_checker.get_report_for_guest(uvm_any) + if global_props.buildkite_pr: + # we only get the uvm_any_a fixtures if we need it + uvm_a = request.getfixturevalue("uvm_any_a") + res_a = spectre_meltdown_checker.get_report_for_guest(uvm_a) + assert res_b <= res_a + else: + assert res_b == spectre_meltdown_checker.expected_vulnerabilities( + uvm_any.cpu_template_name + ) diff --git a/tests/integration_tests/style/test_gitlint.py b/tests/integration_tests/style/test_gitlint.py index b47286d33e9..b7ef8631327 100644 --- a/tests/integration_tests/style/test_gitlint.py +++ b/tests/integration_tests/style/test_gitlint.py @@ -5,6 +5,7 @@ import os from framework import utils +from framework.ab_test import DEFAULT_A_REVISION def test_gitlint(): @@ -15,6 +16,6 @@ def test_gitlint(): os.environ["LANG"] = "C.UTF-8" rc, _, stderr = utils.run_cmd( - "gitlint --commits origin/main..HEAD -C ../.gitlint --extra-path framework/gitlint_rules.py", + f"gitlint --commits origin/{DEFAULT_A_REVISION}..HEAD -C ../.gitlint --extra-path framework/gitlint_rules.py", ) assert rc == 0, "Commit message violates gitlint rules: {}".format(stderr) diff --git a/tests/integration_tests/style/test_licenses.py b/tests/integration_tests/style/test_licenses.py index c95181850f7..182c501da00 100644 --- a/tests/integration_tests/style/test_licenses.py +++ b/tests/integration_tests/style/test_licenses.py @@ -29,6 +29,10 @@ INTEL_LICENSE = "SPDX-License-Identifier: Apache-2.0" RIVOS_COPYRIGHT = "Copyright © 2023 Rivos, Inc." RIVOS_LICENSE = "SPDX-License-Identifier: Apache-2.0" +ORACLE_COPYRIGHT = "Copyright © 2020, Oracle and/or its affiliates." +ORACLE_LICENSE = "SPDX-License-Identifier: Apache-2.0" + +EXCLUDE = ["build", ".kernel", ".git"] def _has_amazon_copyright(string): @@ -90,6 +94,10 @@ def _validate_license(filename): file, RIVOS_LICENSE ) + has_oracle_copyright = ORACLE_COPYRIGHT in copyright_info and _look_for_license( + file, ORACLE_LICENSE + ) + return ( has_amazon_copyright or has_chromium_copyright @@ -97,6 +105,7 @@ def _validate_license(filename): or has_alibaba_copyright or has_intel_copyright or has_rivos_copyright + or has_oracle_copyright ) diff --git a/tests/integration_tests/style/test_python.py b/tests/integration_tests/style/test_python.py index 6a836dc4287..447a1969daa 100644 --- a/tests/integration_tests/style/test_python.py +++ b/tests/integration_tests/style/test_python.py @@ -28,17 +28,7 @@ def test_python_pylint(): Test that python code passes linter checks. """ # List of linter commands that should be executed for each file - linter_cmd = ( - # Pylint - "pylint --jobs=0 --persistent=no --score=no " - '--output-format=colorized --attr-rgx="[a-z_][a-z0-9_]{1,30}$" ' - '--argument-rgx="[a-z_][a-z0-9_]{1,35}$" ' - '--variable-rgx="[a-z_][a-z0-9_]{1,30}$" --disable=' - "fixme,too-many-instance-attributes,import-error," - "too-many-locals,too-many-arguments,consider-using-f-string," - "consider-using-with,implicit-str-concat,line-too-long,redefined-outer-name," - "broad-exception-raised,duplicate-code tests tools .buildkite/*.py" - ) + linter_cmd = "pylint --rcfile tests/pyproject.toml --output-format=colorized tests/ tools/ .buildkite/*.py" run( linter_cmd, # we let pytest capture stdout/stderr for us diff --git a/tests/integration_tests/style/test_repo.py b/tests/integration_tests/style/test_repo.py index 90a1e9450e1..299f89f6cdb 100644 --- a/tests/integration_tests/style/test_repo.py +++ b/tests/integration_tests/style/test_repo.py @@ -44,8 +44,8 @@ def test_repo_validate_changelog(): errors = [] for lineno, line in enumerate(changelog, start=1): if line.startswith("## "): - if not re.match(r"^## \\\[.+\\\]$", line): - msg = "Level 2 headings (versions) should be wrapped in \\[\\]" + if not re.match(r"^## \[.+\]$", line): + msg = "Level 2 headings (versions) should be wrapped in []" errors.append((lineno, msg, line)) if line.startswith("### "): if not re.match(r"^### (Added|Changed|Deprecated|Removed|Fixed)$", line): diff --git a/tests/integration_tests/style/test_rust.py b/tests/integration_tests/style/test_rust.py index 295f2f209f1..580c33eb03d 100644 --- a/tests/integration_tests/style/test_rust.py +++ b/tests/integration_tests/style/test_rust.py @@ -6,11 +6,7 @@ def test_rust_order(): - """ - Tests that `Cargo.toml` dependencies are alphabetically ordered. - - @type: style - """ + """Tests that `Cargo.toml` dependencies are alphabetically ordered.""" # Runs `cargo-sort` with the current working directory (`cwd`) as the repository root. _, _, _ = utils.check_output( @@ -19,9 +15,7 @@ def test_rust_order(): def test_rust_style(): - """ - Test that rust code passes style checks. - """ + """Test that rust code passes style checks.""" # ../src/io_uring/src/bindings.rs config = open("fmt.toml", encoding="utf-8").read().replace("\n", ",") diff --git a/tests/integration_tests/test_kani.py b/tests/integration_tests/test_kani.py index 35c84e105b6..9b087a5375f 100644 --- a/tests/integration_tests/test_kani.py +++ b/tests/integration_tests/test_kani.py @@ -13,10 +13,12 @@ PLATFORM = platform.machine() +TIMEOUT = 3600 + # The `check_output` timeout will always fire before this one, but we need to # set a timeout here to override the default pytest timeout of 180s. -@pytest.mark.timeout(2420) +@pytest.mark.timeout(TIMEOUT) @pytest.mark.skipif( os.environ.get("BUILDKITE") != "true", reason="Kani's memory requirements likely cannot be satisfied locally", @@ -27,13 +29,13 @@ def test_kani(results_dir): """ # -Z stubbing is required to enable the stubbing feature # -Z function-contracts is required to enable the function contracts feature - # --restrict-vtable is required for some virtio queue proofs, which go out of memory otherwise + # -Z restrict-vtable is required for some virtio queue proofs, which go out of memory otherwise # -j enables kani harnesses to be verified in parallel (required to keep CI time low) # --output-format terse is required by -j - # --enable-unstable is needed to enable `-Z` flags + # -Z unstable-options is needed to enable the other `-Z` flags _, stdout, _ = utils.check_output( - "cargo kani --enable-unstable -Z stubbing -Z function-contracts --restrict-vtable -j --output-format terse", - timeout=2400, + "cargo kani -Z unstable-options -Z stubbing -Z function-contracts -Z restrict-vtable -j --output-format terse", + timeout=TIMEOUT, ) (results_dir / "kani_log").write_text(stdout, encoding="utf-8") diff --git a/tests/pyproject.toml b/tests/pyproject.toml index a81f188f996..70df5a75331 100644 --- a/tests/pyproject.toml +++ b/tests/pyproject.toml @@ -8,3 +8,51 @@ exclude = "/(\\.direnv|\\.eggs|\\.git|\\.hg|\\.mypy_cache|\\.nox|\\.tox|\\.venv| # https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes.html multi_line_output = 3 profile = "black" + +[tool.pylint.main] + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs = 0 + +score = false + +# Pickle collected data for later comparisons. +persistent = false + +# Disable the message, report, category or checker with the given id(s). You can +# either give multiple identifiers separated by comma (,) or put this option +# multiple times (only on the command line, not in the configuration file where +# it should appear only once). You can also use "--disable=all" to disable +# everything first and then re-enable specific checks. For example, if you want +# to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable = [ + "raw-checker-failed", + "bad-inline-option", + "locally-disabled", + "file-ignored", + "suppressed-message", + "useless-suppression", + "deprecated-pragma", + "use-implicit-booleaness-not-comparison-to-string", + "use-implicit-booleaness-not-comparison-to-zero", + "use-symbolic-message-instead", + "fixme", + "too-many-instance-attributes", + "import-error", + "too-many-locals", + "too-many-arguments", + "consider-using-f-string", + "consider-using-with", + "implicit-str-concat", + "line-too-long", + "redefined-outer-name", + "broad-exception-raised", + "duplicate-code", + "too-many-positional-arguments", + "too-few-public-methods", +] diff --git a/tools/ab_test.py b/tools/ab_test.py index 2d03d9591a1..fdcb433fe1b 100755 --- a/tools/ab_test.py +++ b/tools/ab_test.py @@ -44,13 +44,8 @@ IGNORED = [ # Network throughput on m6a.metal {"instance": "m6a.metal", "performance_test": "test_network_tcp_throughput"}, - # Block throughput for 1 vcpu on m6g.metal/5.10 - { - "performance_test": "test_block_performance", - "instance": "m6g.metal", - "host_kernel": "linux-5.10", - "vcpus": "1", - }, + # Network throughput on m7a.metal + {"instance": "m7a.metal-48xl", "performance_test": "test_network_tcp_throughput"}, ] @@ -71,7 +66,11 @@ def extract_dimensions(emf): # Skipped tests emit a duration metric, but have no dimensions set return {} - dimension_list = emf["_aws"]["CloudWatchMetrics"][0]["Dimensions"][0] + dimension_list = [ + dim + for dimensions in emf["_aws"]["CloudWatchMetrics"][0]["Dimensions"] + for dim in dimensions + ] return {key: emf[key] for key in emf if key in dimension_list} @@ -143,7 +142,7 @@ def load_data_series(report_path: Path, tag=None, *, reemit: bool = False): # multiple EMF log messages. We need to reassemble :( assert ( processed_emf[dimension_set].keys() == result.keys() - ), f"Found incompatible metrics associated with dimension set {dimension_set}: {processed_emf[dimension_set].key()} in one EMF message, but {result.keys()} in another." + ), f"Found incompatible metrics associated with dimension set {dimension_set}: {processed_emf[dimension_set].keys()} in one EMF message, but {result.keys()} in another." for metric, (values, unit) in processed_emf[dimension_set].items(): assert result[metric][1] == unit diff --git a/tools/bindgen-patches/0001-change-c_char-to-c_uchar-in-ifrn_name.patch b/tools/bindgen-patches/0001-change-c_char-to-c_uchar-in-ifrn_name.patch index 59c5abff2fe..1c05555a384 100644 --- a/tools/bindgen-patches/0001-change-c_char-to-c_uchar-in-ifrn_name.patch +++ b/tools/bindgen-patches/0001-change-c_char-to-c_uchar-in-ifrn_name.patch @@ -1,16 +1,16 @@ -diff --git a/src/vmm/src/devices/virtio/net/gen/iff.rs b/src/vmm/src/devices/virtio/net/gen/iff.rs +diff --git a/src/vmm/src/devices/virtio/net/generated/iff.rs b/src/vmm/src/devices/virtio/net/generated/iff.rs index 04e38396..54111c6b 100644 ---- a/src/vmm/src/devices/virtio/net/gen/iff.rs -+++ b/src/vmm/src/devices/virtio/net/gen/iff.rs -@@ -836,7 +836,7 @@ pub struct ifreq { +--- a/src/vmm/src/devices/virtio/net/generated/iff.rs ++++ b/src/vmm/src/devices/virtio/net/generated/iff.rs +@@ -325,7 +325,7 @@ #[repr(C)] #[derive(Copy, Clone)] pub union ifreq__bindgen_ty_1 { - pub ifrn_name: [::std::os::raw::c_char; 16usize], + pub ifrn_name: [::std::os::raw::c_uchar; 16usize], } - #[test] - fn bindgen_test_layout_ifreq__bindgen_ty_1() { + #[allow(clippy::unnecessary_operation, clippy::identity_op)] + const _: () = { -- 2.40.1 diff --git a/tools/bindgen.sh b/tools/bindgen.sh index e2698b81daf..86817a0561f 100755 --- a/tools/bindgen.sh +++ b/tools/bindgen.sh @@ -34,7 +34,8 @@ function fc-bindgen { clippy::ptr_as_ptr, clippy::undocumented_unsafe_blocks, missing_debug_implementations, - clippy::tests_outside_test_module + clippy::tests_outside_test_module, + unsafe_op_in_unsafe_fn )] EOF @@ -44,14 +45,14 @@ EOF KERNEL_HEADERS_HOME="/usr" info "BINDGEN sockios.h" -fc-bindgen "$KERNEL_HEADERS_HOME/include/linux/sockios.h" |replace_linux_int_types >src/vmm/src/devices/virtio/net/gen/sockios.rs +fc-bindgen "$KERNEL_HEADERS_HOME/include/linux/sockios.h" |replace_linux_int_types >src/vmm/src/devices/virtio/net/generated/sockios.rs info "BINDGEN if.h" fc-bindgen "$KERNEL_HEADERS_HOME/include/linux/if.h" \ --allowlist-var='IF.*' \ --allowlist-type='if.*' \ --allowlist-type="net_device.*" \ - -- -D __UAPI_DEF_IF_IFNAMSIZ -D __UAPI_DEF_IF_NET_DEVICE_FLAGS -D __UAPI_DEF_IF_IFREQ -D __UAPI_DEF_IF_IFMAP >src/vmm/src/devices/virtio/net/gen/iff.rs + -- -D __UAPI_DEF_IF_IFNAMSIZ -D __UAPI_DEF_IF_NET_DEVICE_FLAGS -D __UAPI_DEF_IF_IFREQ -D __UAPI_DEF_IF_IFMAP >src/vmm/src/devices/virtio/net/generated/iff.rs info "BINDGEN if_tun.h" fc-bindgen \ @@ -63,37 +64,34 @@ fc-bindgen \ --allowlist-var='IFF_VNET_HDR' \ --allowlist-var='ETH_.*' \ --allowlist-type='ifreq' \ - "$KERNEL_HEADERS_HOME/include/linux/if_tun.h" >src/vmm/src/devices/virtio/net/gen/if_tun.rs + "$KERNEL_HEADERS_HOME/include/linux/if_tun.h" >src/vmm/src/devices/virtio/net/generated/if_tun.rs info "BINDGEN virtio_ring.h" fc-bindgen \ --allowlist-var "VIRTIO_RING_F_EVENT_IDX" \ - "$KERNEL_HEADERS_HOME/include/linux/virtio_ring.h" >src/vmm/src/devices/virtio/gen/virtio_ring.rs + "$KERNEL_HEADERS_HOME/include/linux/virtio_ring.h" >src/vmm/src/devices/virtio/generated/virtio_ring.rs + +info "BINDGEN virtio_config.h" +fc-bindgen \ + --allowlist-var "VIRTIO_F_.*" \ + "$KERNEL_HEADERS_HOME/include/linux/virtio_config.h" >src/vmm/src/devices/virtio/generated/virtio_config.rs info "BINDGEN virtio_blk.h" fc-bindgen \ --allowlist-var "VIRTIO_BLK_.*" \ - --allowlist-var "VIRTIO_F_.*" \ - "$KERNEL_HEADERS_HOME/include/linux/virtio_blk.h" >src/vmm/src/devices/virtio/gen/virtio_blk.rs + "$KERNEL_HEADERS_HOME/include/linux/virtio_blk.h" >src/vmm/src/devices/virtio/generated/virtio_blk.rs info "BINDGEN virtio_net.h" fc-bindgen \ --allowlist-var "VIRTIO_NET_F_.*" \ - --allowlist-var "VIRTIO_F_.*" \ --allowlist-type "virtio_net_hdr_v1" \ - "$KERNEL_HEADERS_HOME/include/linux/virtio_net.h" >src/vmm/src/devices/virtio/gen/virtio_net.rs - -info "BINDGEN virtio_rng.h" -fc-bindgen \ - --allowlist-var "VIRTIO_RNG_.*" \ - --allowlist-var "VIRTIO_F_.*" \ - "$KERNEL_HEADERS_HOME/include/linux/virtio_rng.h" >src/vmm/src/devices/virtio/gen/virtio_rng.rs + "$KERNEL_HEADERS_HOME/include/linux/virtio_net.h" >src/vmm/src/devices/virtio/generated/virtio_net.rs info "BINDGEN prctl.h" fc-bindgen \ --allowlist-var "PR_.*" \ - "$KERNEL_HEADERS_HOME/include/linux/prctl.h" >src/firecracker/src/gen/prctl.rs -sed -i '/PR_SET_SPECULATION_CTRL/s/u32/i32/g' src/firecracker/src/gen/prctl.rs + "$KERNEL_HEADERS_HOME/include/linux/prctl.h" >src/firecracker/src/generated/prctl.rs +sed -i '/PR_SET_SPECULATION_CTRL/s/u32/i32/g' src/firecracker/src/generated/prctl.rs # https://www.kernel.org/doc/Documentation/kbuild/headers_install.txt # The Linux repo is huge. Just copy what we need. @@ -102,7 +100,7 @@ git clone --branch linux-5.10.y --depth 1 https://github.com/amazonlinux/linux a info "BINDGEN mpspec_def.h" fc-bindgen amazonlinux-v5.10.y/arch/x86/include/asm/mpspec_def.h \ - >src/vmm/src/arch/x86_64/gen/mpspec.rs + >src/vmm/src/arch/x86_64/generated/mpspec.rs # https://github.com/rust-lang/rust-bindgen/issues/1274 info "BINDGEN msr-index.h" @@ -114,8 +112,8 @@ fc-bindgen amazonlinux-v5.10.y/arch/x86/include/asm/msr-index.h \ -Iamazonlinux-v5.10.y/include/ \ -Iamazonlinux-v5.10.y/arch/x86/include/ \ -Wno-macro-redefined \ - >src/vmm/src/arch/x86_64/gen/msr_index.rs -perl -i -pe 's/= (\d+);/sprintf("= 0x%x;",$1)/eg' src/vmm/src/arch/x86_64/gen/msr_index.rs + >src/vmm/src/arch/x86_64/generated/msr_index.rs +perl -i -pe 's/= (\d+);/sprintf("= 0x%x;",$1)/eg' src/vmm/src/arch/x86_64/generated/msr_index.rs info "BINDGEN perf_event.h" grep "MSR_ARCH_PERFMON_" amazonlinux-v5.10.y/arch/x86/include/asm/perf_event.h \ @@ -123,8 +121,8 @@ grep "MSR_ARCH_PERFMON_" amazonlinux-v5.10.y/arch/x86/include/asm/perf_event.h \ fc-bindgen amazonlinux-v5.10.y/arch/x86/include/asm/perf_event_msr.h \ --allowlist-var "^MSR_ARCH_PERFMON_.*$" \ -- \ - >src/vmm/src/arch/x86_64/gen/perf_event.rs -perl -i -pe 's/= (\d+);/sprintf("= 0x%x;",$1)/eg' src/vmm/src/arch/x86_64/gen/perf_event.rs + >src/vmm/src/arch/x86_64/generated/perf_event.rs +perl -i -pe 's/= (\d+);/sprintf("= 0x%x;",$1)/eg' src/vmm/src/arch/x86_64/generated/perf_event.rs info "BINDGEN hyperv.h" grep "HV_X64_MSR_" amazonlinux-v5.10.y/arch/x86/kvm/hyperv.h \ @@ -132,8 +130,8 @@ grep "HV_X64_MSR_" amazonlinux-v5.10.y/arch/x86/kvm/hyperv.h \ fc-bindgen amazonlinux-v5.10.y/arch/x86/kvm/hyperv_msr.h \ --allowlist-var "^HV_X64_MSR_.*$" \ -- \ - >src/vmm/src/arch/x86_64/gen/hyperv.rs -perl -i -pe 's/= (\d+);/sprintf("= 0x%x;",$1)/eg' src/vmm/src/arch/x86_64/gen/hyperv.rs + >src/vmm/src/arch/x86_64/generated/hyperv.rs +perl -i -pe 's/= (\d+);/sprintf("= 0x%x;",$1)/eg' src/vmm/src/arch/x86_64/generated/hyperv.rs info "BINDGEN hyperv-tlfs.h" grep "HV_X64_MSR_" amazonlinux-v5.10.y/arch/x86/include/asm/hyperv-tlfs.h \ @@ -141,8 +139,8 @@ grep "HV_X64_MSR_" amazonlinux-v5.10.y/arch/x86/include/asm/hyperv-tlfs.h \ fc-bindgen amazonlinux-v5.10.y/arch/x86/include/asm/hyperv-tlfs_msr.h \ --allowlist-var "^HV_X64_MSR_.*$" \ -- \ - >src/vmm/src/arch/x86_64/gen/hyperv_tlfs.rs -perl -i -pe 's/= (\d+);/sprintf("= 0x%x;",$1)/eg' src/vmm/src/arch/x86_64/gen/hyperv_tlfs.rs + >src/vmm/src/arch/x86_64/generated/hyperv_tlfs.rs +perl -i -pe 's/= (\d+);/sprintf("= 0x%x;",$1)/eg' src/vmm/src/arch/x86_64/generated/hyperv_tlfs.rs info "BINDGEN io_uring.h" fc-bindgen \ @@ -152,7 +150,16 @@ fc-bindgen \ --allowlist-type "io_uring_.+" \ --allowlist-type "io_.qring_offsets" \ "amazonlinux-v5.10.y/include/uapi/linux/io_uring.h" \ - >src/vmm/src/io_uring/gen.rs + >src/vmm/src/io_uring/generated.rs + +# Latest upstream kernel +KERNEL_SRC_DIR="linux" +[ -d ${KERNEL_SRC_DIR} ] || git clone --depth 1 https://github.com/amazonlinux/linux ${KERNEL_SRC_DIR} + +info "BINDGEN asm/prctl.h" +fc-bindgen \ + --allowlist-var "ARCH_.*" \ + "${KERNEL_SRC_DIR}/arch/x86/include/uapi/asm/prctl.h" >src/vmm/src/arch/x86_64/generated/arch_prctl.rs # Apply any patches info "Apply patches" diff --git a/tools/create_snapshot_artifact/main.py b/tools/create_snapshot_artifact/main.py deleted file mode 100755 index 75d439c1185..00000000000 --- a/tools/create_snapshot_artifact/main.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -"""Script used to generate snapshots of microVMs.""" - -import json -import os -import platform -import re -import shutil -import sys -from pathlib import Path - -# Hack to be able to import testing framework functions. -sys.path.append(os.path.join(os.getcwd(), "tests")) # noqa: E402 - -# pylint: disable=wrong-import-position -from framework.artifacts import disks, kernels -from framework.microvm import MicroVMFactory -from framework.utils import ( - configure_mmds, - generate_mmds_get_request, - generate_mmds_session_token, -) -from framework.utils_cpu_templates import get_supported_cpu_templates -from host_tools.cargo_build import get_firecracker_binaries - -# pylint: enable=wrong-import-position - -# Default IPv4 address to route MMDS requests. -IPV4_ADDRESS = "169.254.169.254" -NET_IFACE_FOR_MMDS = "eth3" -# Root directory for the snapshot artifacts. -SNAPSHOT_ARTIFACTS_ROOT_DIR = "snapshot_artifacts" - - -def populate_mmds(microvm, data_store): - """Populate MMDS contents with json data provided.""" - # MMDS should be empty. - response = microvm.api.mmds.get() - assert response.json() == {} - - # Populate MMDS with data. - microvm.api.mmds.put(**data_store) - - # Ensure data is persistent inside the data store. - response = microvm.api.mmds.get() - assert response.json() == data_store - - -def validate_mmds(ssh_connection, data_store): - """Validate that MMDS contents fetched from the guest.""" - # Configure interface to route MMDS requests - cmd = "ip route add {} dev {}".format(IPV4_ADDRESS, NET_IFACE_FOR_MMDS) - _, stdout, stderr = ssh_connection.run(cmd) - assert stdout == stderr == "" - - # Fetch metadata to ensure MMDS is accessible. - token = generate_mmds_session_token(ssh_connection, IPV4_ADDRESS, token_ttl=60) - - cmd = generate_mmds_get_request(IPV4_ADDRESS, token=token) - _, stdout, _ = ssh_connection.run(cmd) - assert json.loads(stdout) == data_store - - -def main(): - """ - Run the main logic. - - Create snapshot artifacts from complex microVMs with all Firecracker's - functionality enabled. The kernels are parametrized to include all guest - supported versions. - - Artifacts are saved in the following format: - snapshot_artifacts - | - -> __guest_snapshot - | - -> vm.mem - -> vm.vmstate - -> ubuntu-22.04.id_rsa - -> ubuntu-22.04.ext4 - -> __guest_snapshot - | - ... - """ - # Create directory dedicated to store snapshot artifacts for - # each guest kernel version. - print("Cleanup") - shutil.rmtree(SNAPSHOT_ARTIFACTS_ROOT_DIR, ignore_errors=True) - vm_factory = MicroVMFactory(*get_firecracker_binaries()) - - cpu_templates = [] - if platform.machine() == "x86_64": - cpu_templates = ["None"] - cpu_templates += get_supported_cpu_templates() - - for cpu_template in cpu_templates: - for kernel in kernels(glob="vmlinux-*"): - for rootfs in disks(glob="ubuntu-*.squashfs"): - print(kernel, rootfs, cpu_template) - vm = vm_factory.build(kernel, rootfs) - vm.spawn(log_level="Info") - vm.basic_config( - vcpu_count=2, - mem_size_mib=1024, - cpu_template=cpu_template, - track_dirty_pages=True, - ) - # Add 4 network devices - for i in range(4): - vm.add_net_iface() - # Add a vsock device - vm.api.vsock.put(vsock_id="vsock0", guest_cid=3, uds_path="/v.sock") - # Add MMDS - configure_mmds(vm, ["eth3"], version="V2") - # Add a memory balloon. - vm.api.balloon.put( - amount_mib=0, deflate_on_oom=True, stats_polling_interval_s=1 - ) - - vm.start() - - # Populate MMDS. - data_store = { - "latest": { - "meta-data": { - "ami-id": "ami-12345678", - "reservation-id": "r-fea54097", - "local-hostname": "ip-10-251-50-12.ec2.internal", - "public-hostname": "ec2-203-0-113-25.compute-1.amazonaws.com", - } - } - } - populate_mmds(vm, data_store) - - # Iterate and validate connectivity on all ifaces after boot. - for i in range(4): - exit_code, _, _ = vm.ssh_iface(i).run("sync") - assert exit_code == 0 - - # Validate MMDS. - validate_mmds(vm.ssh, data_store) - - # Snapshot the microVM. - snapshot = vm.snapshot_diff() - - # Create snapshot artifacts directory specific for the kernel version used. - guest_kernel_version = re.search("vmlinux-(.*)", kernel.name) - - snapshot_artifacts_dir = ( - Path(SNAPSHOT_ARTIFACTS_ROOT_DIR) - / f"{guest_kernel_version.group(1)}_{cpu_template}_guest_snapshot" - ) - snapshot_artifacts_dir.mkdir(parents=True) - snapshot.save_to(snapshot_artifacts_dir) - print(f"Copied snapshot to: {snapshot_artifacts_dir}.") - - vm.kill() - - -if __name__ == "__main__": - main() diff --git a/tools/devctr/Dockerfile b/tools/devctr/Dockerfile index eef52632a6a..0448f2231dc 100644 --- a/tools/devctr/Dockerfile +++ b/tools/devctr/Dockerfile @@ -4,7 +4,7 @@ FROM public.ecr.aws/lts/ubuntu:24.04 # The Rust toolchain layer will get updated most frequently, but we could keep the system # dependencies layer intact for much longer. -ARG RUST_TOOLCHAIN="1.79.0" +ARG RUST_TOOLCHAIN="1.85.0" ARG TMP_BUILD_DIR=/tmp/build ARG DEBIAN_FRONTEND=noninteractive ARG PIP_BREAK_SYSTEM_PACKAGES=1 @@ -75,7 +75,8 @@ RUN apt-get update \ screen tmux \ tzdata \ tini \ - squashfs-tools \ + squashfs-tools zstd \ + python3-seccomp \ # for aws-lc-rs cmake \ # for Qemu vhost-user-blk backend @@ -83,7 +84,7 @@ RUN apt-get update \ # for crosvm (vhost-user-blk backend) libcap2 \ # for debugging - gdb strace \ + gdb strace trace-cmd \ && rm -rf /var/lib/apt/lists/* \ && pip3 install --upgrade poetry @@ -98,8 +99,13 @@ RUN cd /tmp/poetry \ ENV VIRTUAL_ENV=$VENV ENV PATH=$VENV/bin:$PATH +# apt-get installs it globally, to manually copy it into the venv +RUN cp /usr/lib/python3/dist-packages/seccomp.cpython-312-"$ARCH"-linux-gnu.so "$VENV"/lib/python3.12/site-packages/ + # Running the three as a single dockerfile command to avoid inflation of the image: -# - Install the Rust toolchain. Kani only work on x86, so only try to install it there +# - Install the Rust toolchain. +# - Kani always installs _some_ nightly toolchain, we reuse it for the seccomp filter analysis test. Dynamically +# determine the exact toolchain name, and install more components into it. # - Build and install crosvm (used as vhost-user-blk backend) # - Clean up cargo compilation directories # - Always install both x86_64 and aarch64 musl targets, as our rust-toolchain.toml would force on-the-fly installation of both anyway @@ -107,9 +113,15 @@ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal --default-too && rustup target add x86_64-unknown-linux-musl \ && rustup target add aarch64-unknown-linux-musl \ && rustup component add llvm-tools-preview clippy rustfmt \ - && cargo install --locked cargo-audit cargo-deny grcov cargo-sort cargo-afl \ + && cargo install --locked cargo-audit grcov cargo-sort cargo-afl \ + && cargo install --locked cargo-deny --version 0.17.0 \ && cargo install --locked kani-verifier && cargo kani setup \ \ + && NIGHTLY_TOOLCHAIN=$(rustup toolchain list | grep nightly | tr -d '\n') \ + && rustup component add rust-src --toolchain "$NIGHTLY_TOOLCHAIN" \ + && rustup target add "$ARCH"-unknown-linux-musl --toolchain "$NIGHTLY_TOOLCHAIN" \ + && cargo +"$NIGHTLY_TOOLCHAIN" install cargo-udeps \ + \ && apt-get update \ && apt-get -y install --no-install-recommends \ libcap-dev \ @@ -137,6 +149,28 @@ RUN cd /usr/include/$ARCH-linux-musl \ && ln -s ../linux linux \ && ln -s ../asm-generic asm-generic +# Install static version of libseccomp +# We need to compile from source because +# libseccomp provided by the distribution is not +# compiled with musl-gcc and we need this +# for our musl builds. +# We specify the tag in order to have a fixed version +# of the library. +RUN apt-get update \ + && apt-get -y install \ + libtool gperf \ + && git clone https://github.com/seccomp/libseccomp /tmp/libseccomp \ + && cd /tmp/libseccomp \ + && git checkout tags/v2.5.5 \ + && ./autogen.sh \ + && CC="musl-gcc -static" ./configure --enable-static=yes --enable-shared=false \ + && make install \ + && cd \ + && apt-get purge -y \ + libtool gperf \ + && apt-get autoremove -y \ + && rm -rf /tmp/libseccomp + # Build iperf3-vsock RUN mkdir "$TMP_BUILD_DIR" && cd "$TMP_BUILD_DIR" \ && git clone https://github.com/stefano-garzarella/iperf-vsock \ diff --git a/tools/devctr/ctr_gitconfig b/tools/devctr/ctr_gitconfig index 2ebfb9fcb08..937cb21a308 100644 --- a/tools/devctr/ctr_gitconfig +++ b/tools/devctr/ctr_gitconfig @@ -6,5 +6,4 @@ # https://github.blog/2022-04-12-git-security-vulnerability-announced/ [safe] - directory = /firecracker - directory = /firecracker/.git + directory = * diff --git a/tools/devctr/poetry.lock b/tools/devctr/poetry.lock index fef3e284ade..b0ed4ce0f3b 100644 --- a/tools/devctr/poetry.lock +++ b/tools/devctr/poetry.lock @@ -1,137 +1,131 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" -version = "2.4.0" +version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd"}, - {file = "aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2"}, + {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, + {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, ] [[package]] name = "aiohttp" -version = "3.10.5" +version = "3.11.13" description = "Async http client/server framework (asyncio)" optional = false -python-versions = ">=3.8" -files = [ - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, - {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, - {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, - {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, - {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, - {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, - {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, - {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, - {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511"}, - {file = "aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a"}, - {file = "aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, - {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, - {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, - {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohttp-3.11.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4fe27dbbeec445e6e1291e61d61eb212ee9fed6e47998b27de71d70d3e8777d"}, + {file = "aiohttp-3.11.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9e64ca2dbea28807f8484c13f684a2f761e69ba2640ec49dacd342763cc265ef"}, + {file = "aiohttp-3.11.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9840be675de208d1f68f84d578eaa4d1a36eee70b16ae31ab933520c49ba1325"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28a772757c9067e2aee8a6b2b425d0efaa628c264d6416d283694c3d86da7689"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b88aca5adbf4625e11118df45acac29616b425833c3be7a05ef63a6a4017bfdb"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce10ddfbe26ed5856d6902162f71b8fe08545380570a885b4ab56aecfdcb07f4"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa48dac27f41b36735c807d1ab093a8386701bbf00eb6b89a0f69d9fa26b3671"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89ce611b1eac93ce2ade68f1470889e0173d606de20c85a012bfa24be96cf867"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:78e4dd9c34ec7b8b121854eb5342bac8b02aa03075ae8618b6210a06bbb8a115"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:66047eacbc73e6fe2462b77ce39fc170ab51235caf331e735eae91c95e6a11e4"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ad8f1c19fe277eeb8bc45741c6d60ddd11d705c12a4d8ee17546acff98e0802"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64815c6f02e8506b10113ddbc6b196f58dbef135751cc7c32136df27b736db09"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:967b93f21b426f23ca37329230d5bd122f25516ae2f24a9cea95a30023ff8283"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf1f31f83d16ec344136359001c5e871915c6ab685a3d8dee38e2961b4c81730"}, + {file = "aiohttp-3.11.13-cp310-cp310-win32.whl", hash = "sha256:00c8ac69e259c60976aa2edae3f13d9991cf079aaa4d3cd5a49168ae3748dee3"}, + {file = "aiohttp-3.11.13-cp310-cp310-win_amd64.whl", hash = "sha256:90d571c98d19a8b6e793b34aa4df4cee1e8fe2862d65cc49185a3a3d0a1a3996"}, + {file = "aiohttp-3.11.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b35aab22419ba45f8fc290d0010898de7a6ad131e468ffa3922b1b0b24e9d2e"}, + {file = "aiohttp-3.11.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81cba651db8795f688c589dd11a4fbb834f2e59bbf9bb50908be36e416dc760"}, + {file = "aiohttp-3.11.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f55d0f242c2d1fcdf802c8fabcff25a9d85550a4cf3a9cf5f2a6b5742c992839"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4bea08a6aad9195ac9b1be6b0c7e8a702a9cec57ce6b713698b4a5afa9c2e33"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6070bcf2173a7146bb9e4735b3c62b2accba459a6eae44deea0eb23e0035a23"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:718d5deb678bc4b9d575bfe83a59270861417da071ab44542d0fcb6faa686636"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f6b2c5b4a4d22b8fb2c92ac98e0747f5f195e8e9448bfb7404cd77e7bfa243f"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:747ec46290107a490d21fe1ff4183bef8022b848cf9516970cb31de6d9460088"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:01816f07c9cc9d80f858615b1365f8319d6a5fd079cd668cc58e15aafbc76a54"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a08ad95fcbd595803e0c4280671d808eb170a64ca3f2980dd38e7a72ed8d1fea"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c97be90d70f7db3aa041d720bfb95f4869d6063fcdf2bb8333764d97e319b7d0"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ab915a57c65f7a29353c8014ac4be685c8e4a19e792a79fe133a8e101111438e"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:35cda4e07f5e058a723436c4d2b7ba2124ab4e0aa49e6325aed5896507a8a42e"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:af55314407714fe77a68a9ccaab90fdb5deb57342585fd4a3a8102b6d4370080"}, + {file = "aiohttp-3.11.13-cp311-cp311-win32.whl", hash = "sha256:42d689a5c0a0c357018993e471893e939f555e302313d5c61dfc566c2cad6185"}, + {file = "aiohttp-3.11.13-cp311-cp311-win_amd64.whl", hash = "sha256:b73a2b139782a07658fbf170fe4bcdf70fc597fae5ffe75e5b67674c27434a9f"}, + {file = "aiohttp-3.11.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2eabb269dc3852537d57589b36d7f7362e57d1ece308842ef44d9830d2dc3c90"}, + {file = "aiohttp-3.11.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b77ee42addbb1c36d35aca55e8cc6d0958f8419e458bb70888d8c69a4ca833d"}, + {file = "aiohttp-3.11.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55789e93c5ed71832e7fac868167276beadf9877b85697020c46e9a75471f55f"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c929f9a7249a11e4aa5c157091cfad7f49cc6b13f4eecf9b747104befd9f56f2"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d33851d85537bbf0f6291ddc97926a754c8f041af759e0aa0230fe939168852b"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9229d8613bd8401182868fe95688f7581673e1c18ff78855671a4b8284f47bcb"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669dd33f028e54fe4c96576f406ebb242ba534dd3a981ce009961bf49960f117"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c1b20a1ace54af7db1f95af85da530fe97407d9063b7aaf9ce6a32f44730778"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5724cc77f4e648362ebbb49bdecb9e2b86d9b172c68a295263fa072e679ee69d"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:aa36c35e94ecdb478246dd60db12aba57cfcd0abcad43c927a8876f25734d496"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9b5b37c863ad5b0892cc7a4ceb1e435e5e6acd3f2f8d3e11fa56f08d3c67b820"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e06cf4852ce8c4442a59bae5a3ea01162b8fcb49ab438d8548b8dc79375dad8a"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5194143927e494616e335d074e77a5dac7cd353a04755330c9adc984ac5a628e"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afcb6b275c2d2ba5d8418bf30a9654fa978b4f819c2e8db6311b3525c86fe637"}, + {file = "aiohttp-3.11.13-cp312-cp312-win32.whl", hash = "sha256:7104d5b3943c6351d1ad7027d90bdd0ea002903e9f610735ac99df3b81f102ee"}, + {file = "aiohttp-3.11.13-cp312-cp312-win_amd64.whl", hash = "sha256:47dc018b1b220c48089b5b9382fbab94db35bef2fa192995be22cbad3c5730c8"}, + {file = "aiohttp-3.11.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9862d077b9ffa015dbe3ce6c081bdf35135948cb89116e26667dd183550833d1"}, + {file = "aiohttp-3.11.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fbfef0666ae9e07abfa2c54c212ac18a1f63e13e0760a769f70b5717742f3ece"}, + {file = "aiohttp-3.11.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:93a1f7d857c4fcf7cabb1178058182c789b30d85de379e04f64c15b7e88d66fb"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba40b7ae0f81c7029583a338853f6607b6d83a341a3dcde8bed1ea58a3af1df9"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5b95787335c483cd5f29577f42bbe027a412c5431f2f80a749c80d040f7ca9f"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7d474c5c1f0b9405c1565fafdc4429fa7d986ccbec7ce55bc6a330f36409cad"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e83fb1991e9d8982b3b36aea1e7ad27ea0ce18c14d054c7a404d68b0319eebb"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4586a68730bd2f2b04a83e83f79d271d8ed13763f64b75920f18a3a677b9a7f0"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fe4eb0e7f50cdb99b26250d9328faef30b1175a5dbcfd6d0578d18456bac567"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2a8a6bc19818ac3e5596310ace5aa50d918e1ebdcc204dc96e2f4d505d51740c"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f27eec42f6c3c1df09cfc1f6786308f8b525b8efaaf6d6bd76c1f52c6511f6a"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2a4a13dfbb23977a51853b419141cd0a9b9573ab8d3a1455c6e63561387b52ff"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:02876bf2f69b062584965507b07bc06903c2dc93c57a554b64e012d636952654"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b992778d95b60a21c4d8d4a5f15aaab2bd3c3e16466a72d7f9bfd86e8cea0d4b"}, + {file = "aiohttp-3.11.13-cp313-cp313-win32.whl", hash = "sha256:507ab05d90586dacb4f26a001c3abf912eb719d05635cbfad930bdbeb469b36c"}, + {file = "aiohttp-3.11.13-cp313-cp313-win_amd64.whl", hash = "sha256:5ceb81a4db2decdfa087381b5fc5847aa448244f973e5da232610304e199e7b2"}, + {file = "aiohttp-3.11.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:51c3ff9c7a25f3cad5c09d9aacbc5aefb9267167c4652c1eb737989b554fe278"}, + {file = "aiohttp-3.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e271beb2b1dabec5cd84eb488bdabf9758d22ad13471e9c356be07ad139b3012"}, + {file = "aiohttp-3.11.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0e9eb7e5764abcb49f0e2bd8f5731849b8728efbf26d0cac8e81384c95acec3f"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baae005092e3f200de02699314ac8933ec20abf998ec0be39448f6605bce93df"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1982c98ac62c132d2b773d50e2fcc941eb0b8bad3ec078ce7e7877c4d5a2dce7"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2b25b2eeb35707113b2d570cadc7c612a57f1c5d3e7bb2b13870fe284e08fc0"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b27961d65639128336b7a7c3f0046dcc62a9443d5ef962e3c84170ac620cec47"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01fe9f1e05025eacdd97590895e2737b9f851d0eb2e017ae9574d9a4f0b6252"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa1fb1b61881c8405829c50e9cc5c875bfdbf685edf57a76817dfb50643e4a1a"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:25de43bb3cf83ad83efc8295af7310219af6dbe4c543c2e74988d8e9c8a2a917"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fe7065e2215e4bba63dc00db9ae654c1ba3950a5fff691475a32f511142fcddb"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7836587eef675a17d835ec3d98a8c9acdbeb2c1d72b0556f0edf4e855a25e9c1"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:85fa0b18558eb1427090912bd456a01f71edab0872f4e0f9e4285571941e4090"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a86dc177eb4c286c19d1823ac296299f59ed8106c9536d2b559f65836e0fb2c6"}, + {file = "aiohttp-3.11.13-cp39-cp39-win32.whl", hash = "sha256:684eea71ab6e8ade86b9021bb62af4bf0881f6be4e926b6b5455de74e420783a"}, + {file = "aiohttp-3.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:82c249f2bfa5ecbe4a1a7902c81c0fba52ed9ebd0176ab3047395d02ad96cfcb"}, + {file = "aiohttp-3.11.13.tar.gz", hash = "sha256:8ce789231404ca8fff7f693cdce398abf6d90fd5dae2b1847477196c243b1fbb"}, ] [package.dependencies] aiohappyeyeballs = ">=2.3.0" aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] [[package]] name = "aiosignal" -version = "1.3.1" +version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, + {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, + {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, ] [package.dependencies] @@ -143,6 +137,7 @@ version = "1.2.3" description = "Better dates & times for Python" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "arrow-1.2.3-py3-none-any.whl", hash = "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2"}, {file = "arrow-1.2.3.tar.gz", hash = "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1"}, @@ -153,13 +148,14 @@ python-dateutil = ">=2.7.0" [[package]] name = "astroid" -version = "3.2.4" +version = "3.3.9" description = "An abstract syntax tree for Python with inference support." optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" +groups = ["main"] files = [ - {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, - {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, + {file = "astroid-3.3.9-py3-none-any.whl", hash = "sha256:d05bfd0acba96a7bd43e222828b7d9bc1e138aaeb0649707908d3702a9831248"}, + {file = "astroid-3.3.9.tar.gz", hash = "sha256:622cc8e3048684aa42c820d9d218978021c3c3d174fb03a9f0d615921744f550"}, ] [package.dependencies] @@ -167,61 +163,63 @@ typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false -python-versions = "*" +python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, + {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, + {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -[package.dependencies] -six = ">=1.12.0" - [package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] +astroid = ["astroid (>=2,<4)"] +test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "async-timeout" -version = "4.0.3" +version = "5.0.1" description = "Timeout context manager for asyncio programs" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.11\"" files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, ] [[package]] name = "attrs" -version = "24.2.0" +version = "25.2.0" description = "Classes Without Boilerplate" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, + {file = "attrs-25.2.0-py3-none-any.whl", hash = "sha256:611344ff0a5fed735d86d7784610c84f8126b95e549bcad9ff61b4242f2d386b"}, + {file = "attrs-25.2.0.tar.gz", hash = "sha256:18a06db706db43ac232cce80443fcd9f2500702059ecf53489e3c5a3f417acaf"}, ] [package.extras] benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "aws-embedded-metrics" -version = "3.2.0" +version = "3.3.0" description = "AWS Embedded Metrics Package" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ - {file = "aws-embedded-metrics-3.2.0.tar.gz", hash = "sha256:f235f87ab25ff328f6f3afca1c6b3218e81eea6e96e6aee012d368bb813fae7b"}, - {file = "aws_embedded_metrics-3.2.0-py3-none-any.whl", hash = "sha256:887b76d24914efa5fc42a7b77983e77fc670633e6e1195aac7653c425fee7399"}, + {file = "aws-embedded-metrics-3.3.0.tar.gz", hash = "sha256:f417c396b394959f923d5b53cfe241d01a0ccded60c8f30d9231ff05c8235ec5"}, + {file = "aws_embedded_metrics-3.3.0-py3-none-any.whl", hash = "sha256:03901a28786a93e718ddb7342a917c3a3c8204c31195edb36151d8b7eef361b3"}, ] [package.dependencies] @@ -229,33 +227,34 @@ aiohttp = "*" [[package]] name = "black" -version = "24.8.0" +version = "24.10.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, - {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, - {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, - {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, - {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, - {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, - {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, - {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, - {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, - {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, - {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, - {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, - {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, - {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, - {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, - {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, - {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, - {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, - {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, - {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, - {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, - {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] [package.dependencies] @@ -269,118 +268,122 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.8.30" +version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] [[package]] @@ -389,6 +392,7 @@ version = "8.1.3" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, @@ -403,6 +407,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -410,24 +416,26 @@ files = [ [[package]] name = "decorator" -version = "5.1.1" +version = "5.2.1" description = "Decorators for Humans" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, + {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, + {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, ] [[package]] name = "dill" -version = "0.3.8" +version = "0.3.9" description = "serialize all of Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, + {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, + {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, ] [package.extras] @@ -440,6 +448,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -454,6 +464,7 @@ version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, @@ -464,13 +475,14 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "executing" -version = "2.1.0" +version = "2.2.0" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, - {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, + {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"}, + {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"}, ] [package.extras] @@ -478,104 +490,121 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "filelock" -version = "3.16.0" +version = "3.17.0" description = "A platform independent file lock." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, - {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, + {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, + {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "frozenlist" -version = "1.4.1" +version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" -files = [ - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, - {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, - {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, - {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, - {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, - {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, - {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, - {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, - {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, - {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, - {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, - {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, - {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +groups = ["main"] +files = [ + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, + {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, + {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, + {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, + {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, + {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, + {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, ] [[package]] @@ -584,6 +613,7 @@ version = "0.19.1" description = "Git commit message linter written in python, checks your commit messages for style." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "gitlint-0.19.1-py3-none-any.whl", hash = "sha256:26bb085959148d99fbbc178b4e56fda6c3edd7646b7c2a24d8ee1f8e036ed85d"}, {file = "gitlint-0.19.1.tar.gz", hash = "sha256:b5b70fb894e80849b69abbb65ee7dbb3520fc3511f202a6e6b6ddf1a71ee8f61"}, @@ -598,6 +628,7 @@ version = "0.19.1" description = "Git commit message linter written in python, checks your commit messages for style." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "gitlint_core-0.19.1-py3-none-any.whl", hash = "sha256:f41effd1dcbc06ffbfc56b6888cce72241796f517b46bd9fd4ab1b145056988c"}, {file = "gitlint_core-0.19.1.tar.gz", hash = "sha256:7bf977b03ff581624a9e03f65ebb8502cc12dfaa3e92d23e8b2b54bbdaa29992"}, @@ -622,21 +653,26 @@ trusted-deps = ["arrow (==1.2.3)", "click (==8.1.3)", "sh (==1.14.3)"] [[package]] name = "idna" -version = "3.8" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ - {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, - {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -644,13 +680,14 @@ files = [ [[package]] name = "ipython" -version = "8.27.0" +version = "8.34.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ - {file = "ipython-8.27.0-py3-none-any.whl", hash = "sha256:f68b3cb8bde357a5d7adc9598d57e22a45dfbea19eb6b98286fa3b288c9cd55c"}, - {file = "ipython-8.27.0.tar.gz", hash = "sha256:0b99a2dc9f15fd68692e898e5568725c6d49c527d36a9fb5960ffbdeaa82ff7e"}, + {file = "ipython-8.34.0-py3-none-any.whl", hash = "sha256:0419883fa46e0baa182c5d50ebb8d6b49df1889fdb70750ad6d8cfe678eda6e3"}, + {file = "ipython-8.34.0.tar.gz", hash = "sha256:c31d658e754673ecc6514583e7dda8069e47136eb62458816b7d1e6625948b5a"}, ] [package.dependencies] @@ -660,16 +697,16 @@ exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} -prompt-toolkit = ">=3.0.41,<3.1.0" +prompt_toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" -stack-data = "*" +stack_data = "*" traitlets = ">=5.13.0" -typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} +typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"] kernel = ["ipykernel"] matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] @@ -686,6 +723,7 @@ version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, @@ -696,22 +734,23 @@ colors = ["colorama (>=0.4.6)"] [[package]] name = "jedi" -version = "0.19.1" +version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, + {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, + {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, ] [package.dependencies] -parso = ">=0.8.3,<0.9.0" +parso = ">=0.8.4,<0.9.0" [package.extras] docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] [[package]] name = "jsonschema" @@ -719,6 +758,7 @@ version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -736,30 +776,32 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema-path" -version = "0.3.3" +version = "0.3.4" description = "JSONSchema Spec with object-oriented paths" optional = false python-versions = "<4.0.0,>=3.8.0" +groups = ["main"] files = [ - {file = "jsonschema_path-0.3.3-py3-none-any.whl", hash = "sha256:203aff257f8038cd3c67be614fe6b2001043408cb1b4e36576bc4921e09d83c4"}, - {file = "jsonschema_path-0.3.3.tar.gz", hash = "sha256:f02e5481a4288ec062f8e68c808569e427d905bedfecb7f2e4c69ef77957c382"}, + {file = "jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8"}, + {file = "jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001"}, ] [package.dependencies] pathable = ">=0.4.1,<0.5.0" PyYAML = ">=5.1" -referencing = ">=0.28.0,<0.36.0" +referencing = "<0.37.0" requests = ">=2.31.0,<3.0.0" [[package]] name = "jsonschema-specifications" -version = "2023.12.1" +version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, - {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, + {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, + {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, ] [package.dependencies] @@ -771,6 +813,7 @@ version = "1.10.0" description = "A fast and thorough lazy object proxy." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, @@ -817,6 +860,7 @@ version = "2.0.3" description = "Links recognition library with FULL unicode support." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"}, {file = "linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"}, @@ -837,6 +881,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -862,6 +907,7 @@ version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, @@ -876,6 +922,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -883,13 +930,14 @@ files = [ [[package]] name = "mdformat" -version = "0.7.17" +version = "0.7.22" description = "CommonMark compliant Markdown formatter" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "mdformat-0.7.17-py3-none-any.whl", hash = "sha256:91ffc5e203f5814a6ad17515c77767fd2737fc12ffd8b58b7bb1d8b9aa6effaa"}, - {file = "mdformat-0.7.17.tar.gz", hash = "sha256:a9dbb1838d43bb1e6f03bd5dca9412c552544a9bc42d6abb5dc32adfe8ae7c0d"}, + {file = "mdformat-0.7.22-py3-none-any.whl", hash = "sha256:61122637c9e1d9be1329054f3fa216559f0d1f722b7919b060a8c2a4ae1850e5"}, + {file = "mdformat-0.7.22.tar.gz", hash = "sha256:eef84fa8f233d3162734683c2a8a6222227a229b9206872e6139658d99acb1ea"}, ] [package.dependencies] @@ -902,6 +950,7 @@ version = "0.1.1" description = "An mdformat plugin for parsing/validating footnotes" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "mdformat_footnote-0.1.1-py3-none-any.whl", hash = "sha256:30063aaa0f74c36257c2e80fa0cf00d7c71a5277f27e98109e8765ae8678a95b"}, {file = "mdformat_footnote-0.1.1.tar.gz", hash = "sha256:3b85c4c84119f15f0b651df89c99a4f6f119fc46dca6b33f7edf4f09655d1126"}, @@ -921,6 +970,7 @@ version = "2.0.8" description = "An mdformat plugin for parsing / ignoring frontmatter." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "mdformat_frontmatter-2.0.8-py3-none-any.whl", hash = "sha256:577396695af96ad66dff1ff781284ff3764a10be3ab8659f2ef842ab42264ebb"}, {file = "mdformat_frontmatter-2.0.8.tar.gz", hash = "sha256:c11190ae3f9c91ada78fbd820f5b221631b520484e0b644715aa0f6ed7f097ed"}, @@ -937,13 +987,14 @@ test = ["coverage", "pytest (>=7.3)", "pytest-cov"] [[package]] name = "mdformat-gfm" -version = "0.3.6" +version = "0.3.7" description = "Mdformat plugin for GitHub Flavored Markdown compatibility" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "mdformat_gfm-0.3.6-py3-none-any.whl", hash = "sha256:579e3619bedd3b7123df888b6929ab8ac5dfc8205d0b67153b1633262bdafc42"}, - {file = "mdformat_gfm-0.3.6.tar.gz", hash = "sha256:b405ebf651a15c186ca06712100e33bbe72afeafb02aa4a4a28ea26cc3219678"}, + {file = "mdformat_gfm-0.3.7-py3-none-any.whl", hash = "sha256:c40966ef26e334226961ab77908dc9697ed63668f6383a18c80cca1cb4bb5c10"}, + {file = "mdformat_gfm-0.3.7.tar.gz", hash = "sha256:7deb2cd1d5334541af5454e52e116639796fc441ddc08e4415f967955950fe10"}, ] [package.dependencies] @@ -958,6 +1009,7 @@ version = "1.0.0" description = "An mdformat plugin for rendering tables." optional = false python-versions = ">=3.7.0" +groups = ["main"] files = [ {file = "mdformat_tables-1.0.0-py3-none-any.whl", hash = "sha256:94cd86126141b2adc3b04c08d1441eb1272b36c39146bab078249a41c7240a9a"}, {file = "mdformat_tables-1.0.0.tar.gz", hash = "sha256:a57db1ac17c4a125da794ef45539904bb8a9592e80557d525e1f169c96daa2c8"}, @@ -976,6 +1028,7 @@ version = "0.4.2" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, @@ -995,6 +1048,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -1006,6 +1060,7 @@ version = "6.1.0" description = "multidict implementation" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, @@ -1110,6 +1165,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -1117,80 +1173,84 @@ files = [ [[package]] name = "numpy" -version = "2.1.1" +version = "2.2.3" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" -files = [ - {file = "numpy-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9"}, - {file = "numpy-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd"}, - {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f"}, - {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab"}, - {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7"}, - {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6"}, - {file = "numpy-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0"}, - {file = "numpy-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647"}, - {file = "numpy-2.1.1-cp310-cp310-win32.whl", hash = "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728"}, - {file = "numpy-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95"}, - {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca"}, - {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf"}, - {file = "numpy-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e"}, - {file = "numpy-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2"}, - {file = "numpy-2.1.1-cp311-cp311-win32.whl", hash = "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d"}, - {file = "numpy-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521"}, - {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b"}, - {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201"}, - {file = "numpy-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a"}, - {file = "numpy-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313"}, - {file = "numpy-2.1.1-cp312-cp312-win32.whl", hash = "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed"}, - {file = "numpy-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0"}, - {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb"}, - {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df"}, - {file = "numpy-2.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78"}, - {file = "numpy-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556"}, - {file = "numpy-2.1.1-cp313-cp313-win32.whl", hash = "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b"}, - {file = "numpy-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468"}, - {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef"}, - {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f"}, - {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c"}, - {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39"}, - {file = "numpy-2.1.1.tar.gz", hash = "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd"}, +groups = ["main"] +files = [ + {file = "numpy-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cbc6472e01952d3d1b2772b720428f8b90e2deea8344e854df22b0618e9cce71"}, + {file = "numpy-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdfe0c22692a30cd830c0755746473ae66c4a8f2e7bd508b35fb3b6a0813d787"}, + {file = "numpy-2.2.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:e37242f5324ffd9f7ba5acf96d774f9276aa62a966c0bad8dae692deebec7716"}, + {file = "numpy-2.2.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:95172a21038c9b423e68be78fd0be6e1b97674cde269b76fe269a5dfa6fadf0b"}, + {file = "numpy-2.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b47c440210c5d1d67e1cf434124e0b5c395eee1f5806fdd89b553ed1acd0a3"}, + {file = "numpy-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0391ea3622f5c51a2e29708877d56e3d276827ac5447d7f45e9bc4ade8923c52"}, + {file = "numpy-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f6b3dfc7661f8842babd8ea07e9897fe3d9b69a1d7e5fbb743e4160f9387833b"}, + {file = "numpy-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1ad78ce7f18ce4e7df1b2ea4019b5817a2f6a8a16e34ff2775f646adce0a5027"}, + {file = "numpy-2.2.3-cp310-cp310-win32.whl", hash = "sha256:5ebeb7ef54a7be11044c33a17b2624abe4307a75893c001a4800857956b41094"}, + {file = "numpy-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:596140185c7fa113563c67c2e894eabe0daea18cf8e33851738c19f70ce86aeb"}, + {file = "numpy-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:16372619ee728ed67a2a606a614f56d3eabc5b86f8b615c79d01957062826ca8"}, + {file = "numpy-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5521a06a3148686d9269c53b09f7d399a5725c47bbb5b35747e1cb76326b714b"}, + {file = "numpy-2.2.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:7c8dde0ca2f77828815fd1aedfdf52e59071a5bae30dac3b4da2a335c672149a"}, + {file = "numpy-2.2.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:77974aba6c1bc26e3c205c2214f0d5b4305bdc719268b93e768ddb17e3fdd636"}, + {file = "numpy-2.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d42f9c36d06440e34226e8bd65ff065ca0963aeecada587b937011efa02cdc9d"}, + {file = "numpy-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2712c5179f40af9ddc8f6727f2bd910ea0eb50206daea75f58ddd9fa3f715bb"}, + {file = "numpy-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c8b0451d2ec95010d1db8ca733afc41f659f425b7f608af569711097fd6014e2"}, + {file = "numpy-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9b4a8148c57ecac25a16b0e11798cbe88edf5237b0df99973687dd866f05e1b"}, + {file = "numpy-2.2.3-cp311-cp311-win32.whl", hash = "sha256:1f45315b2dc58d8a3e7754fe4e38b6fce132dab284a92851e41b2b344f6441c5"}, + {file = "numpy-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f48ba6f6c13e5e49f3d3efb1b51c8193215c42ac82610a04624906a9270be6f"}, + {file = "numpy-2.2.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12c045f43b1d2915eca6b880a7f4a256f59d62df4f044788c8ba67709412128d"}, + {file = "numpy-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87eed225fd415bbae787f93a457af7f5990b92a334e346f72070bf569b9c9c95"}, + {file = "numpy-2.2.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:712a64103d97c404e87d4d7c47fb0c7ff9acccc625ca2002848e0d53288b90ea"}, + {file = "numpy-2.2.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a5ae282abe60a2db0fd407072aff4599c279bcd6e9a2475500fc35b00a57c532"}, + {file = "numpy-2.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5266de33d4c3420973cf9ae3b98b54a2a6d53a559310e3236c4b2b06b9c07d4e"}, + {file = "numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe"}, + {file = "numpy-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:34c1b7e83f94f3b564b35f480f5652a47007dd91f7c839f404d03279cc8dd021"}, + {file = "numpy-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4d8335b5f1b6e2bce120d55fb17064b0262ff29b459e8493d1785c18ae2553b8"}, + {file = "numpy-2.2.3-cp312-cp312-win32.whl", hash = "sha256:4d9828d25fb246bedd31e04c9e75714a4087211ac348cb39c8c5f99dbb6683fe"}, + {file = "numpy-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d"}, + {file = "numpy-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bfdb06b395385ea9b91bf55c1adf1b297c9fdb531552845ff1d3ea6e40d5aba"}, + {file = "numpy-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23c9f4edbf4c065fddb10a4f6e8b6a244342d95966a48820c614891e5059bb50"}, + {file = "numpy-2.2.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:a0c03b6be48aaf92525cccf393265e02773be8fd9551a2f9adbe7db1fa2b60f1"}, + {file = "numpy-2.2.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:2376e317111daa0a6739e50f7ee2a6353f768489102308b0d98fcf4a04f7f3b5"}, + {file = "numpy-2.2.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fb62fe3d206d72fe1cfe31c4a1106ad2b136fcc1606093aeab314f02930fdf2"}, + {file = "numpy-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52659ad2534427dffcc36aac76bebdd02b67e3b7a619ac67543bc9bfe6b7cdb1"}, + {file = "numpy-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b416af7d0ed3271cad0f0a0d0bee0911ed7eba23e66f8424d9f3dfcdcae1304"}, + {file = "numpy-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1402da8e0f435991983d0a9708b779f95a8c98c6b18a171b9f1be09005e64d9d"}, + {file = "numpy-2.2.3-cp313-cp313-win32.whl", hash = "sha256:136553f123ee2951bfcfbc264acd34a2fc2f29d7cdf610ce7daf672b6fbaa693"}, + {file = "numpy-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5b732c8beef1d7bc2d9e476dbba20aaff6167bf205ad9aa8d30913859e82884b"}, + {file = "numpy-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:435e7a933b9fda8126130b046975a968cc2d833b505475e588339e09f7672890"}, + {file = "numpy-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7678556eeb0152cbd1522b684dcd215250885993dd00adb93679ec3c0e6e091c"}, + {file = "numpy-2.2.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2e8da03bd561504d9b20e7a12340870dfc206c64ea59b4cfee9fceb95070ee94"}, + {file = "numpy-2.2.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:c9aa4496fd0e17e3843399f533d62857cef5900facf93e735ef65aa4bbc90ef0"}, + {file = "numpy-2.2.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4ca91d61a4bf61b0f2228f24bbfa6a9facd5f8af03759fe2a655c50ae2c6610"}, + {file = "numpy-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaa09cd492e24fd9b15296844c0ad1b3c976da7907e1c1ed3a0ad21dded6f76"}, + {file = "numpy-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:246535e2f7496b7ac85deffe932896a3577be7af8fb7eebe7146444680297e9a"}, + {file = "numpy-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:daf43a3d1ea699402c5a850e5313680ac355b4adc9770cd5cfc2940e7861f1bf"}, + {file = "numpy-2.2.3-cp313-cp313t-win32.whl", hash = "sha256:cf802eef1f0134afb81fef94020351be4fe1d6681aadf9c5e862af6602af64ef"}, + {file = "numpy-2.2.3-cp313-cp313t-win_amd64.whl", hash = "sha256:aee2512827ceb6d7f517c8b85aa5d3923afe8fc7a57d028cffcd522f1c6fd082"}, + {file = "numpy-2.2.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3c2ec8a0f51d60f1e9c0c5ab116b7fc104b165ada3f6c58abf881cb2eb16044d"}, + {file = "numpy-2.2.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ed2cf9ed4e8ebc3b754d398cba12f24359f018b416c380f577bbae112ca52fc9"}, + {file = "numpy-2.2.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39261798d208c3095ae4f7bc8eaeb3481ea8c6e03dc48028057d3cbdbdb8937e"}, + {file = "numpy-2.2.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:783145835458e60fa97afac25d511d00a1eca94d4a8f3ace9fe2043003c678e4"}, + {file = "numpy-2.2.3.tar.gz", hash = "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020"}, ] [[package]] name = "openapi-schema-validator" -version = "0.6.2" +version = "0.6.3" description = "OpenAPI schema validation for Python" optional = false -python-versions = ">=3.8.0,<4.0.0" +python-versions = "<4.0.0,>=3.8.0" +groups = ["main"] files = [ - {file = "openapi_schema_validator-0.6.2-py3-none-any.whl", hash = "sha256:c4887c1347c669eb7cded9090f4438b710845cd0f90d1fb9e1b3303fb37339f8"}, - {file = "openapi_schema_validator-0.6.2.tar.gz", hash = "sha256:11a95c9c9017912964e3e5f2545a5b11c3814880681fcacfb73b1759bb4f2804"}, + {file = "openapi_schema_validator-0.6.3-py3-none-any.whl", hash = "sha256:f3b9870f4e556b5a62a1c39da72a6b4b16f3ad9c73dc80084b1b11e74ba148a3"}, + {file = "openapi_schema_validator-0.6.3.tar.gz", hash = "sha256:f37bace4fc2a5d96692f4f8b31dc0f8d7400fd04f3a937798eaf880d425de6ee"}, ] [package.dependencies] jsonschema = ">=4.19.1,<5.0.0" -jsonschema-specifications = ">=2023.5.2,<2024.0.0" +jsonschema-specifications = ">=2023.5.2" rfc3339-validator = "*" [[package]] @@ -1199,6 +1259,7 @@ version = "0.7.1" description = "OpenAPI 2.0 (aka Swagger) and OpenAPI 3 spec validator" optional = false python-versions = ">=3.8.0,<4.0.0" +groups = ["main"] files = [ {file = "openapi_spec_validator-0.7.1-py3-none-any.whl", hash = "sha256:3c81825043f24ccbcd2f4b149b11e8231abce5ba84f37065e14ec947d8f4e959"}, {file = "openapi_spec_validator-0.7.1.tar.gz", hash = "sha256:8577b85a8268685da6f8aa30990b83b7960d4d1117e901d451b5d572605e5ec7"}, @@ -1212,13 +1273,14 @@ openapi-schema-validator = ">=0.6.0,<0.7.0" [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -1227,6 +1289,7 @@ version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, @@ -1238,13 +1301,14 @@ testing = ["docopt", "pytest"] [[package]] name = "pathable" -version = "0.4.3" +version = "0.4.4" description = "Object-oriented paths" optional = false -python-versions = ">=3.7.0,<4.0.0" +python-versions = "<4.0.0,>=3.7.0" +groups = ["main"] files = [ - {file = "pathable-0.4.3-py3-none-any.whl", hash = "sha256:cdd7b1f9d7d5c8b8d3315dbf5a86b2596053ae845f056f57d97c0eefff84da14"}, - {file = "pathable-0.4.3.tar.gz", hash = "sha256:5c869d315be50776cc8a993f3af43e0c60dc01506b399643f919034ebf4cdcab"}, + {file = "pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2"}, + {file = "pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2"}, ] [[package]] @@ -1253,6 +1317,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1264,6 +1329,8 @@ version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\"" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -1274,13 +1341,14 @@ ptyprocess = ">=0.5" [[package]] name = "platformdirs" -version = "4.3.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, - {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] @@ -1294,6 +1362,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1305,46 +1374,157 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prompt-toolkit" -version = "3.0.47" +version = "3.0.50" description = "Library for building powerful interactive command lines in Python" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8.0" +groups = ["main"] files = [ - {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, - {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, + {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, + {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, ] [package.dependencies] wcwidth = "*" +[[package]] +name = "propcache" +version = "0.3.0" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:efa44f64c37cc30c9f05932c740a8b40ce359f51882c70883cc95feac842da4d"}, + {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2383a17385d9800b6eb5855c2f05ee550f803878f344f58b6e194de08b96352c"}, + {file = "propcache-0.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e7420211f5a65a54675fd860ea04173cde60a7cc20ccfbafcccd155225f8bc"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3302c5287e504d23bb0e64d2a921d1eb4a03fb93a0a0aa3b53de059f5a5d737d"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e2e068a83552ddf7a39a99488bcba05ac13454fb205c847674da0352602082f"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d913d36bdaf368637b4f88d554fb9cb9d53d6920b9c5563846555938d5450bf"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ee1983728964d6070ab443399c476de93d5d741f71e8f6e7880a065f878e0b9"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36ca5e9a21822cc1746023e88f5c0af6fce3af3b85d4520efb1ce4221bed75cc"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9ecde3671e62eeb99e977f5221abcf40c208f69b5eb986b061ccec317c82ebd0"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d383bf5e045d7f9d239b38e6acadd7b7fdf6c0087259a84ae3475d18e9a2ae8b"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8cb625bcb5add899cb8ba7bf716ec1d3e8f7cdea9b0713fa99eadf73b6d4986f"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5fa159dcee5dba00c1def3231c249cf261185189205073bde13797e57dd7540a"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a7080b0159ce05f179cfac592cda1a82898ca9cd097dacf8ea20ae33474fbb25"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed7161bccab7696a473fe7ddb619c1d75963732b37da4618ba12e60899fefe4f"}, + {file = "propcache-0.3.0-cp310-cp310-win32.whl", hash = "sha256:bf0d9a171908f32d54f651648c7290397b8792f4303821c42a74e7805bfb813c"}, + {file = "propcache-0.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:42924dc0c9d73e49908e35bbdec87adedd651ea24c53c29cac103ede0ea1d340"}, + {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ddd49258610499aab83b4f5b61b32e11fce873586282a0e972e5ab3bcadee51"}, + {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2578541776769b500bada3f8a4eeaf944530516b6e90c089aa368266ed70c49e"}, + {file = "propcache-0.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8074c5dd61c8a3e915fa8fc04754fa55cfa5978200d2daa1e2d4294c1f136aa"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b58229a844931bca61b3a20efd2be2a2acb4ad1622fc026504309a6883686fbf"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e45377d5d6fefe1677da2a2c07b024a6dac782088e37c0b1efea4cfe2b1be19b"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec5060592d83454e8063e487696ac3783cc48c9a329498bafae0d972bc7816c9"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15010f29fbed80e711db272909a074dc79858c6d28e2915704cfc487a8ac89c6"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a254537b9b696ede293bfdbc0a65200e8e4507bc9f37831e2a0318a9b333c85c"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2b975528998de037dfbc10144b8aed9b8dd5a99ec547f14d1cb7c5665a43f075"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:19d36bb351ad5554ff20f2ae75f88ce205b0748c38b146c75628577020351e3c"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6032231d4a5abd67c7f71168fd64a47b6b451fbcb91c8397c2f7610e67683810"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6985a593417cdbc94c7f9c3403747335e450c1599da1647a5af76539672464d3"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a1948df1bb1d56b5e7b0553c0fa04fd0e320997ae99689488201f19fa90d2e7"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8319293e85feadbbfe2150a5659dbc2ebc4afdeaf7d98936fb9a2f2ba0d4c35c"}, + {file = "propcache-0.3.0-cp311-cp311-win32.whl", hash = "sha256:63f26258a163c34542c24808f03d734b338da66ba91f410a703e505c8485791d"}, + {file = "propcache-0.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:cacea77ef7a2195f04f9279297684955e3d1ae4241092ff0cfcef532bb7a1c32"}, + {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e53d19c2bf7d0d1e6998a7e693c7e87300dd971808e6618964621ccd0e01fe4e"}, + {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a61a68d630e812b67b5bf097ab84e2cd79b48c792857dc10ba8a223f5b06a2af"}, + {file = "propcache-0.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb91d20fa2d3b13deea98a690534697742029f4fb83673a3501ae6e3746508b5"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67054e47c01b7b349b94ed0840ccae075449503cf1fdd0a1fdd98ab5ddc2667b"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997e7b8f173a391987df40f3b52c423e5850be6f6df0dcfb5376365440b56667"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d663fd71491dde7dfdfc899d13a067a94198e90695b4321084c6e450743b8c7"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8884ba1a0fe7210b775106b25850f5e5a9dc3c840d1ae9924ee6ea2eb3acbfe7"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa806bbc13eac1ab6291ed21ecd2dd426063ca5417dd507e6be58de20e58dfcf"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f4d7a7c0aff92e8354cceca6fe223973ddf08401047920df0fcb24be2bd5138"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9be90eebc9842a93ef8335291f57b3b7488ac24f70df96a6034a13cb58e6ff86"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bf15fc0b45914d9d1b706f7c9c4f66f2b7b053e9517e40123e137e8ca8958b3d"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a16167118677d94bb48bfcd91e420088854eb0737b76ec374b91498fb77a70e"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:41de3da5458edd5678b0f6ff66691507f9885f5fe6a0fb99a5d10d10c0fd2d64"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:728af36011bb5d344c4fe4af79cfe186729efb649d2f8b395d1572fb088a996c"}, + {file = "propcache-0.3.0-cp312-cp312-win32.whl", hash = "sha256:6b5b7fd6ee7b54e01759f2044f936dcf7dea6e7585f35490f7ca0420fe723c0d"}, + {file = "propcache-0.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:2d15bc27163cd4df433e75f546b9ac31c1ba7b0b128bfb1b90df19082466ff57"}, + {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a2b9bf8c79b660d0ca1ad95e587818c30ccdb11f787657458d6f26a1ea18c568"}, + {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0c1a133d42c6fc1f5fbcf5c91331657a1ff822e87989bf4a6e2e39b818d0ee9"}, + {file = "propcache-0.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bb2f144c6d98bb5cbc94adeb0447cfd4c0f991341baa68eee3f3b0c9c0e83767"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1323cd04d6e92150bcc79d0174ce347ed4b349d748b9358fd2e497b121e03c8"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b812b3cb6caacd072276ac0492d249f210006c57726b6484a1e1805b3cfeea0"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:742840d1d0438eb7ea4280f3347598f507a199a35a08294afdcc560c3739989d"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6e7e4f9167fddc438cd653d826f2222222564daed4116a02a184b464d3ef05"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a94ffc66738da99232ddffcf7910e0f69e2bbe3a0802e54426dbf0714e1c2ffe"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c6ec957025bf32b15cbc6b67afe233c65b30005e4c55fe5768e4bb518d712f1"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:549722908de62aa0b47a78b90531c022fa6e139f9166be634f667ff45632cc92"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5d62c4f6706bff5d8a52fd51fec6069bef69e7202ed481486c0bc3874912c787"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:24c04f8fbf60094c531667b8207acbae54146661657a1b1be6d3ca7773b7a545"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7c5f5290799a3f6539cc5e6f474c3e5c5fbeba74a5e1e5be75587746a940d51e"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4fa0e7c9c3cf7c276d4f6ab9af8adddc127d04e0fcabede315904d2ff76db626"}, + {file = "propcache-0.3.0-cp313-cp313-win32.whl", hash = "sha256:ee0bd3a7b2e184e88d25c9baa6a9dc609ba25b76daae942edfb14499ac7ec374"}, + {file = "propcache-0.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c8f7d896a16da9455f882870a507567d4f58c53504dc2d4b1e1d386dfe4588a"}, + {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e560fd75aaf3e5693b91bcaddd8b314f4d57e99aef8a6c6dc692f935cc1e6bbf"}, + {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:65a37714b8ad9aba5780325228598a5b16c47ba0f8aeb3dc0514701e4413d7c0"}, + {file = "propcache-0.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:07700939b2cbd67bfb3b76a12e1412405d71019df00ca5697ce75e5ef789d829"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c0fdbdf6983526e269e5a8d53b7ae3622dd6998468821d660d0daf72779aefa"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:794c3dd744fad478b6232289c866c25406ecdfc47e294618bdf1697e69bd64a6"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4544699674faf66fb6b4473a1518ae4999c1b614f0b8297b1cef96bac25381db"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddb8870bdb83456a489ab67c6b3040a8d5a55069aa6f72f9d872235fbc52f54"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f857034dc68d5ceb30fb60afb6ff2103087aea10a01b613985610e007053a121"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02df07041e0820cacc8f739510078f2aadcfd3fc57eaeeb16d5ded85c872c89e"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f47d52fd9b2ac418c4890aad2f6d21a6b96183c98021f0a48497a904199f006e"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9ff4e9ecb6e4b363430edf2c6e50173a63e0820e549918adef70515f87ced19a"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ecc2920630283e0783c22e2ac94427f8cca29a04cfdf331467d4f661f4072dac"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:c441c841e82c5ba7a85ad25986014be8d7849c3cfbdb6004541873505929a74e"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c929916cbdb540d3407c66f19f73387f43e7c12fa318a66f64ac99da601bcdf"}, + {file = "propcache-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:0c3e893c4464ebd751b44ae76c12c5f5c1e4f6cbd6fbf67e3783cd93ad221863"}, + {file = "propcache-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:75e872573220d1ee2305b35c9813626e620768248425f58798413e9c39741f46"}, + {file = "propcache-0.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:03c091bb752349402f23ee43bb2bff6bd80ccab7c9df6b88ad4322258d6960fc"}, + {file = "propcache-0.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46ed02532cb66612d42ae5c3929b5e98ae330ea0f3900bc66ec5f4862069519b"}, + {file = "propcache-0.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11ae6a8a01b8a4dc79093b5d3ca2c8a4436f5ee251a9840d7790dccbd96cb649"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df03cd88f95b1b99052b52b1bb92173229d7a674df0ab06d2b25765ee8404bce"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03acd9ff19021bd0567582ac88f821b66883e158274183b9e5586f678984f8fe"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd54895e4ae7d32f1e3dd91261df46ee7483a735017dc6f987904f194aa5fd14"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a67e5c04e3119594d8cfae517f4b9330c395df07ea65eab16f3d559b7068fe"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee25f1ac091def37c4b59d192bbe3a206298feeb89132a470325bf76ad122a1e"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58e6d2a5a7cb3e5f166fd58e71e9a4ff504be9dc61b88167e75f835da5764d07"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:be90c94570840939fecedf99fa72839aed70b0ced449b415c85e01ae67422c90"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:49ea05212a529c2caffe411e25a59308b07d6e10bf2505d77da72891f9a05641"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:119e244ab40f70a98c91906d4c1f4c5f2e68bd0b14e7ab0a06922038fae8a20f"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:507c5357a8d8b4593b97fb669c50598f4e6cccbbf77e22fa9598aba78292b4d7"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8526b0941ec5a40220fc4dfde76aed58808e2b309c03e9fa8e2260083ef7157f"}, + {file = "propcache-0.3.0-cp39-cp39-win32.whl", hash = "sha256:7cedd25e5f678f7738da38037435b340694ab34d424938041aa630d8bac42663"}, + {file = "propcache-0.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:bf4298f366ca7e1ad1d21bbb58300a6985015909964077afd37559084590c929"}, + {file = "propcache-0.3.0-py3-none-any.whl", hash = "sha256:67dda3c7325691c2081510e92c561f465ba61b975f481735aefdfc845d2cd043"}, + {file = "propcache-0.3.0.tar.gz", hash = "sha256:a8fd93de4e1d278046345f49e2238cdb298589325849b2645d4a94c53faeffc5"}, +] + [[package]] name = "psutil" -version = "6.0.0" +version = "6.1.1" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, - {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, - {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, - {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, - {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, - {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, - {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, - {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, - {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, - {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, +groups = ["main"] +files = [ + {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"}, + {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"}, + {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8df0178ba8a9e5bc84fed9cfa61d54601b371fbec5c8eebad27575f1e105c0d4"}, + {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:1924e659d6c19c647e763e78670a05dbb7feaf44a0e9c94bf9e14dfc6ba50468"}, + {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca"}, + {file = "psutil-6.1.1-cp27-none-win32.whl", hash = "sha256:6d4281f5bbca041e2292be3380ec56a9413b790579b8e593b1784499d0005dac"}, + {file = "psutil-6.1.1-cp27-none-win_amd64.whl", hash = "sha256:c777eb75bb33c47377c9af68f30e9f11bc78e0f07fbf907be4a5d70b2fe5f030"}, + {file = "psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"}, + {file = "psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3"}, + {file = "psutil-6.1.1-cp36-cp36m-win32.whl", hash = "sha256:384636b1a64b47814437d1173be1427a7c83681b17a450bfc309a1953e329603"}, + {file = "psutil-6.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8be07491f6ebe1a693f17d4f11e69d0dc1811fa082736500f649f79df7735303"}, + {file = "psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53"}, + {file = "psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649"}, + {file = "psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5"}, ] [package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +dev = ["abi3audit", "black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] [[package]] name = "ptyprocess" @@ -1352,6 +1532,8 @@ version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\"" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -1363,6 +1545,7 @@ version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, @@ -1373,13 +1556,14 @@ tests = ["pytest"] [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, ] [package.extras] @@ -1387,24 +1571,25 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" -version = "3.2.7" +version = "3.3.5" description = "python code static checker" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" +groups = ["main"] files = [ - {file = "pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b"}, - {file = "pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e"}, + {file = "pylint-3.3.5-py3-none-any.whl", hash = "sha256:7cb170929a371238530b2eeea09f5f28236d106b70308c3d46a9c0cf11634633"}, + {file = "pylint-3.3.5.tar.gz", hash = "sha256:38d0f784644ed493d91f76b5333a0e370a1c1bc97c22068a77523b4bf1e82c31"}, ] [package.dependencies] -astroid = ">=3.2.4,<=3.3.0-dev0" +astroid = ">=3.3.8,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +isort = ">=4.2.5,<5.13.0 || >5.13.0,<7" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -1416,13 +1601,14 @@ testutils = ["gitpython (>3)"] [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, + {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, ] [package.dependencies] @@ -1442,6 +1628,7 @@ version = "1.5.0" description = "A pytest plugin to report test results as JSON files" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pytest-json-report-1.5.0.tar.gz", hash = "sha256:2dde3c647851a19b5f3700729e8310a6e66efb2077d674f27ddea3d34dc615de"}, {file = "pytest_json_report-1.5.0-py3-none-any.whl", hash = "sha256:9897b68c910b12a2e48dd849f9a284b2c79a732a8a9cb398452ddd23d3c8c325"}, @@ -1457,6 +1644,7 @@ version = "3.1.1" description = "pytest plugin for test session metadata" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, @@ -1474,6 +1662,7 @@ version = "14.0" description = "pytest plugin to re-run tests to eliminate flaky failures" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pytest-rerunfailures-14.0.tar.gz", hash = "sha256:4a400bcbcd3c7a4ad151ab8afac123d90eca3abe27f98725dc4d9702887d2e92"}, {file = "pytest_rerunfailures-14.0-py3-none-any.whl", hash = "sha256:4197bdd2eaeffdbf50b5ea6e7236f47ff0e44d1def8dae08e409f536d84e7b32"}, @@ -1489,6 +1678,7 @@ version = "2.3.1" description = "pytest plugin to abort hanging tests" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9"}, {file = "pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e"}, @@ -1503,6 +1693,7 @@ version = "3.6.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, @@ -1523,6 +1714,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -1537,6 +1729,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1595,18 +1788,20 @@ files = [ [[package]] name = "referencing" -version = "0.35.1" +version = "0.36.2" description = "JSON Referencing + Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, - {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, + {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, + {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, ] [package.dependencies] attrs = ">=22.2.0" rpds-py = ">=0.7.0" +typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} [[package]] name = "requests" @@ -1614,6 +1809,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1635,6 +1831,7 @@ version = "0.4.2" description = "Use requests to talk HTTP via a UNIX domain socket" optional = false python-versions = "<4.0.0,>=3.8.1" +groups = ["main"] files = [ {file = "requests_unixsocket2-0.4.2-py3-none-any.whl", hash = "sha256:701fcd49d74bc0f759bbe45c4dfda0045fd89652948c2b473b1a312214c3770b"}, {file = "requests_unixsocket2-0.4.2.tar.gz", hash = "sha256:929c58ecc5981f3d127661ceb9ec8c76e0f08d31c52e44ab1462ac0dcd55b5f5"}, @@ -1650,6 +1847,7 @@ version = "0.1.4" description = "A pure python RFC3339 validator" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] files = [ {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, @@ -1660,125 +1858,127 @@ six = "*" [[package]] name = "rpds-py" -version = "0.20.0" +version = "0.23.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false -python-versions = ">=3.8" -files = [ - {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, - {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, - {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, - {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, - {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, - {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, - {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, - {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, - {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, - {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, - {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, - {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, - {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, - {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, - {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "rpds_py-0.23.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2a54027554ce9b129fc3d633c92fa33b30de9f08bc61b32c053dc9b537266fed"}, + {file = "rpds_py-0.23.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5ef909a37e9738d146519657a1aab4584018746a18f71c692f2f22168ece40c"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ee9d6f0b38efb22ad94c3b68ffebe4c47865cdf4b17f6806d6c674e1feb4246"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7356a6da0562190558c4fcc14f0281db191cdf4cb96e7604c06acfcee96df15"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9441af1d25aed96901f97ad83d5c3e35e6cd21a25ca5e4916c82d7dd0490a4fa"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d8abf7896a91fb97e7977d1aadfcc2c80415d6dc2f1d0fca5b8d0df247248f3"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b08027489ba8fedde72ddd233a5ea411b85a6ed78175f40285bd401bde7466d"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fee513135b5a58f3bb6d89e48326cd5aa308e4bcdf2f7d59f67c861ada482bf8"}, + {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:35d5631ce0af26318dba0ae0ac941c534453e42f569011585cb323b7774502a5"}, + {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a20cb698c4a59c534c6701b1c24a968ff2768b18ea2991f886bd8985ce17a89f"}, + {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e9c206a1abc27e0588cf8b7c8246e51f1a16a103734f7750830a1ccb63f557a"}, + {file = "rpds_py-0.23.1-cp310-cp310-win32.whl", hash = "sha256:d9f75a06ecc68f159d5d7603b734e1ff6daa9497a929150f794013aa9f6e3f12"}, + {file = "rpds_py-0.23.1-cp310-cp310-win_amd64.whl", hash = "sha256:f35eff113ad430b5272bbfc18ba111c66ff525828f24898b4e146eb479a2cdda"}, + {file = "rpds_py-0.23.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b79f5ced71efd70414a9a80bbbfaa7160da307723166f09b69773153bf17c590"}, + {file = "rpds_py-0.23.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c9e799dac1ffbe7b10c1fd42fe4cd51371a549c6e108249bde9cd1200e8f59b4"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721f9c4011b443b6e84505fc00cc7aadc9d1743f1c988e4c89353e19c4a968ee"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f88626e3f5e57432e6191cd0c5d6d6b319b635e70b40be2ffba713053e5147dd"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:285019078537949cecd0190f3690a0b0125ff743d6a53dfeb7a4e6787af154f5"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92f5654157de1379c509b15acec9d12ecf6e3bc1996571b6cb82a4302060447"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e768267cbe051dd8d1c5305ba690bb153204a09bf2e3de3ae530de955f5b5580"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c5334a71f7dc1160382d45997e29f2637c02f8a26af41073189d79b95d3321f1"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6adb81564af0cd428910f83fa7da46ce9ad47c56c0b22b50872bc4515d91966"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cafa48f2133d4daa028473ede7d81cd1b9f9e6925e9e4003ebdf77010ee02f35"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fced9fd4a07a1ded1bac7e961ddd9753dd5d8b755ba8e05acba54a21f5f1522"}, + {file = "rpds_py-0.23.1-cp311-cp311-win32.whl", hash = "sha256:243241c95174b5fb7204c04595852fe3943cc41f47aa14c3828bc18cd9d3b2d6"}, + {file = "rpds_py-0.23.1-cp311-cp311-win_amd64.whl", hash = "sha256:11dd60b2ffddba85715d8a66bb39b95ddbe389ad2cfcf42c833f1bcde0878eaf"}, + {file = "rpds_py-0.23.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3902df19540e9af4cc0c3ae75974c65d2c156b9257e91f5101a51f99136d834c"}, + {file = "rpds_py-0.23.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66f8d2a17e5838dd6fb9be6baaba8e75ae2f5fa6b6b755d597184bfcd3cb0eba"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:112b8774b0b4ee22368fec42749b94366bd9b536f8f74c3d4175d4395f5cbd31"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0df046f2266e8586cf09d00588302a32923eb6386ced0ca5c9deade6af9a149"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3288930b947cbebe767f84cf618d2cbe0b13be476e749da0e6a009f986248c"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce473a2351c018b06dd8d30d5da8ab5a0831056cc53b2006e2a8028172c37ce5"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d550d7e9e7d8676b183b37d65b5cd8de13676a738973d330b59dc8312df9c5dc"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e14f86b871ea74c3fddc9a40e947d6a5d09def5adc2076ee61fb910a9014fb35"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf5be5ba34e19be579ae873da515a2836a2166d8d7ee43be6ff909eda42b72b"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7031d493c4465dbc8d40bd6cafefef4bd472b17db0ab94c53e7909ee781b9ef"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55ff4151cfd4bc635e51cfb1c59ac9f7196b256b12e3a57deb9e5742e65941ad"}, + {file = "rpds_py-0.23.1-cp312-cp312-win32.whl", hash = "sha256:a9d3b728f5a5873d84cba997b9d617c6090ca5721caaa691f3b1a78c60adc057"}, + {file = "rpds_py-0.23.1-cp312-cp312-win_amd64.whl", hash = "sha256:b03a8d50b137ee758e4c73638b10747b7c39988eb8e6cd11abb7084266455165"}, + {file = "rpds_py-0.23.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4caafd1a22e5eaa3732acb7672a497123354bef79a9d7ceed43387d25025e935"}, + {file = "rpds_py-0.23.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:178f8a60fc24511c0eb756af741c476b87b610dba83270fce1e5a430204566a4"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c632419c3870507ca20a37c8f8f5352317aca097639e524ad129f58c125c61c6"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:698a79d295626ee292d1730bc2ef6e70a3ab135b1d79ada8fde3ed0047b65a10"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271fa2184cf28bdded86bb6217c8e08d3a169fe0bbe9be5e8d96e8476b707122"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b91cceb5add79ee563bd1f70b30896bd63bc5f78a11c1f00a1e931729ca4f1f4"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a6cb95074777f1ecda2ca4fa7717caa9ee6e534f42b7575a8f0d4cb0c24013"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50fb62f8d8364978478b12d5f03bf028c6bc2af04082479299139dc26edf4c64"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8f7e90b948dc9dcfff8003f1ea3af08b29c062f681c05fd798e36daa3f7e3e8"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5b98b6c953e5c2bda51ab4d5b4f172617d462eebc7f4bfdc7c7e6b423f6da957"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2893d778d4671ee627bac4037a075168b2673c57186fb1a57e993465dbd79a93"}, + {file = "rpds_py-0.23.1-cp313-cp313-win32.whl", hash = "sha256:2cfa07c346a7ad07019c33fb9a63cf3acb1f5363c33bc73014e20d9fe8b01cdd"}, + {file = "rpds_py-0.23.1-cp313-cp313-win_amd64.whl", hash = "sha256:3aaf141d39f45322e44fc2c742e4b8b4098ead5317e5f884770c8df0c332da70"}, + {file = "rpds_py-0.23.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:759462b2d0aa5a04be5b3e37fb8183615f47014ae6b116e17036b131985cb731"}, + {file = "rpds_py-0.23.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3e9212f52074fc9d72cf242a84063787ab8e21e0950d4d6709886fb62bcb91d5"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e9f3a3ac919406bc0414bbbd76c6af99253c507150191ea79fab42fdb35982a"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c04ca91dda8a61584165825907f5c967ca09e9c65fe8966ee753a3f2b019fe1e"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab923167cfd945abb9b51a407407cf19f5bee35001221f2911dc85ffd35ff4f"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed6f011bedca8585787e5082cce081bac3d30f54520097b2411351b3574e1219"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959bb9928c5c999aba4a3f5a6799d571ddc2c59ff49917ecf55be2bbb4e3722"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ed7de3c86721b4e83ac440751329ec6a1102229aa18163f84c75b06b525ad7e"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5fb89edee2fa237584e532fbf78f0ddd1e49a47c7c8cfa153ab4849dc72a35e6"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7e5413d2e2d86025e73f05510ad23dad5950ab8417b7fc6beaad99be8077138b"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d31ed4987d72aabdf521eddfb6a72988703c091cfc0064330b9e5f8d6a042ff5"}, + {file = "rpds_py-0.23.1-cp313-cp313t-win32.whl", hash = "sha256:f3429fb8e15b20961efca8c8b21432623d85db2228cc73fe22756c6637aa39e7"}, + {file = "rpds_py-0.23.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6f6512a90bd5cd9030a6237f5346f046c6f0e40af98657568fa45695d4de59d"}, + {file = "rpds_py-0.23.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:09cd7dbcb673eb60518231e02874df66ec1296c01a4fcd733875755c02014b19"}, + {file = "rpds_py-0.23.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6760211eee3a76316cf328f5a8bd695b47b1626d21c8a27fb3b2473a884d597"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e680c1518733b73c994361e4b06441b92e973ef7d9449feec72e8ee4f713da"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae28144c1daa61366205d32abd8c90372790ff79fc60c1a8ad7fd3c8553a600e"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c698d123ce5d8f2d0cd17f73336615f6a2e3bdcedac07a1291bb4d8e7d82a05a"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98b257ae1e83f81fb947a363a274c4eb66640212516becaff7bef09a5dceacaa"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c9ff044eb07c8468594d12602291c635da292308c8c619244e30698e7fc455a"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7938c7b0599a05246d704b3f5e01be91a93b411d0d6cc62275f025293b8a11ce"}, + {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e9cb79ecedfc156c0692257ac7ed415243b6c35dd969baa461a6888fc79f2f07"}, + {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7b77e07233925bd33fc0022b8537774423e4c6680b6436316c5075e79b6384f4"}, + {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a970bfaf130c29a679b1d0a6e0f867483cea455ab1535fb427566a475078f27f"}, + {file = "rpds_py-0.23.1-cp39-cp39-win32.whl", hash = "sha256:4233df01a250b3984465faed12ad472f035b7cd5240ea3f7c76b7a7016084495"}, + {file = "rpds_py-0.23.1-cp39-cp39-win_amd64.whl", hash = "sha256:c617d7453a80e29d9973b926983b1e700a9377dbe021faa36041c78537d7b08c"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c1f8afa346ccd59e4e5630d5abb67aba6a9812fddf764fd7eb11f382a345f8cc"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fad784a31869747df4ac968a351e070c06ca377549e4ace94775aaa3ab33ee06"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5a96fcac2f18e5a0a23a75cd27ce2656c66c11c127b0318e508aab436b77428"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e77febf227a1dc3220159355dba68faa13f8dca9335d97504abf428469fb18b"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26bb3e8de93443d55e2e748e9fd87deb5f8075ca7bc0502cfc8be8687d69a2ec"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db7707dde9143a67b8812c7e66aeb2d843fe33cc8e374170f4d2c50bd8f2472d"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eedaaccc9bb66581d4ae7c50e15856e335e57ef2734dbc5fd8ba3e2a4ab3cb6"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28358c54fffadf0ae893f6c1050e8f8853e45df22483b7fff2f6ab6152f5d8bf"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:633462ef7e61d839171bf206551d5ab42b30b71cac8f10a64a662536e057fdef"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a98f510d86f689fcb486dc59e6e363af04151e5260ad1bdddb5625c10f1e95f8"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e0397dd0b3955c61ef9b22838144aa4bef6f0796ba5cc8edfc64d468b93798b4"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:75307599f0d25bf6937248e5ac4e3bde5ea72ae6618623b86146ccc7845ed00b"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3614d280bf7aab0d3721b5ce0e73434acb90a2c993121b6e81a1c15c665298ac"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e5963ea87f88bddf7edd59644a35a0feecf75f8985430124c253612d4f7d27ae"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad76f44f70aac3a54ceb1813ca630c53415da3a24fd93c570b2dfb4856591017"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c6ae11e6e93728d86aafc51ced98b1658a0080a7dd9417d24bfb955bb09c3c2"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc869af5cba24d45fb0399b0cfdbcefcf6910bf4dee5d74036a57cf5264b3ff4"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76b32eb2ab650a29e423525e84eb197c45504b1c1e6e17b6cc91fcfeb1a4b1d"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4263320ed887ed843f85beba67f8b2d1483b5947f2dc73a8b068924558bfeace"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7f9682a8f71acdf59fd554b82b1c12f517118ee72c0f3944eda461606dfe7eb9"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:754fba3084b70162a6b91efceee8a3f06b19e43dac3f71841662053c0584209a"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:a1c66e71ecfd2a4acf0e4bd75e7a3605afa8f9b28a3b497e4ba962719df2be57"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8d67beb6002441faef8251c45e24994de32c4c8686f7356a1f601ad7c466f7c3"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a1e17d8dc8e57d8e0fd21f8f0f0a5211b3fa258b2e444c2053471ef93fe25a00"}, + {file = "rpds_py-0.23.1.tar.gz", hash = "sha256:7f3240dcfa14d198dba24b8b9cb3b108c06b68d45b7babd9eefc1038fdf7e707"}, ] [[package]] name = "ruamel-yaml" -version = "0.18.6" +version = "0.18.10" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, - {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, + {file = "ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1"}, + {file = "ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58"}, ] [package.dependencies] @@ -1790,208 +1990,218 @@ jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] [[package]] name = "ruamel-yaml-clib" -version = "0.2.8" +version = "0.2.12" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" optional = false -python-versions = ">=3.6" -files = [ - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, - {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, - {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, +python-versions = ">=3.9" +groups = ["main"] +markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\"" +files = [ + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"}, + {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, ] [[package]] name = "scipy" -version = "1.14.1" +version = "1.15.2" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.10" -files = [ - {file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"}, - {file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"}, - {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"}, - {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"}, - {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"}, - {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"}, - {file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"}, - {file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"}, - {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"}, - {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"}, - {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"}, - {file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"}, - {file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"}, - {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"}, - {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"}, - {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"}, - {file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"}, - {file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"}, - {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"}, - {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"}, - {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"}, - {file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"}, - {file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"}, - {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, +groups = ["main"] +files = [ + {file = "scipy-1.15.2-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a2ec871edaa863e8213ea5df811cd600734f6400b4af272e1c011e69401218e9"}, + {file = "scipy-1.15.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:6f223753c6ea76983af380787611ae1291e3ceb23917393079dcc746ba60cfb5"}, + {file = "scipy-1.15.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:ecf797d2d798cf7c838c6d98321061eb3e72a74710e6c40540f0e8087e3b499e"}, + {file = "scipy-1.15.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:9b18aa747da280664642997e65aab1dd19d0c3d17068a04b3fe34e2559196cb9"}, + {file = "scipy-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87994da02e73549dfecaed9e09a4f9d58a045a053865679aeb8d6d43747d4df3"}, + {file = "scipy-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69ea6e56d00977f355c0f84eba69877b6df084516c602d93a33812aa04d90a3d"}, + {file = "scipy-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:888307125ea0c4466287191e5606a2c910963405ce9671448ff9c81c53f85f58"}, + {file = "scipy-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9412f5e408b397ff5641080ed1e798623dbe1ec0d78e72c9eca8992976fa65aa"}, + {file = "scipy-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:b5e025e903b4f166ea03b109bb241355b9c42c279ea694d8864d033727205e65"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:92233b2df6938147be6fa8824b8136f29a18f016ecde986666be5f4d686a91a4"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:62ca1ff3eb513e09ed17a5736929429189adf16d2d740f44e53270cc800ecff1"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4c6676490ad76d1c2894d77f976144b41bd1a4052107902238047fb6a473e971"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8bf5cb4a25046ac61d38f8d3c3426ec11ebc350246a4642f2f315fe95bda655"}, + {file = "scipy-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a8e34cf4c188b6dd004654f88586d78f95639e48a25dfae9c5e34a6dc34547e"}, + {file = "scipy-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28a0d2c2075946346e4408b211240764759e0fabaeb08d871639b5f3b1aca8a0"}, + {file = "scipy-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:42dabaaa798e987c425ed76062794e93a243be8f0f20fff6e7a89f4d61cb3d40"}, + {file = "scipy-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f5e296ec63c5da6ba6fa0343ea73fd51b8b3e1a300b0a8cae3ed4b1122c7462"}, + {file = "scipy-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:597a0c7008b21c035831c39927406c6181bcf8f60a73f36219b69d010aa04737"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c4697a10da8f8765bb7c83e24a470da5797e37041edfd77fd95ba3811a47c4fd"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:869269b767d5ee7ea6991ed7e22b3ca1f22de73ab9a49c44bad338b725603301"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bad78d580270a4d32470563ea86c6590b465cb98f83d760ff5b0990cb5518a93"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b09ae80010f52efddb15551025f9016c910296cf70adbf03ce2a8704f3a5ad20"}, + {file = "scipy-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6fd6eac1ce74a9f77a7fc724080d507c5812d61e72bd5e4c489b042455865e"}, + {file = "scipy-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b871df1fe1a3ba85d90e22742b93584f8d2b8e6124f8372ab15c71b73e428b8"}, + {file = "scipy-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:03205d57a28e18dfd39f0377d5002725bf1f19a46f444108c29bdb246b6c8a11"}, + {file = "scipy-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:601881dfb761311045b03114c5fe718a12634e5608c3b403737ae463c9885d53"}, + {file = "scipy-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c68b6a43259ba0aab737237876e5c2c549a031ddb7abc28c7b47f22e202ded"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01edfac9f0798ad6b46d9c4c9ca0e0ad23dbf0b1eb70e96adb9fa7f525eff0bf"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:08b57a9336b8e79b305a143c3655cc5bdbe6d5ece3378578888d2afbb51c4e37"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:54c462098484e7466362a9f1672d20888f724911a74c22ae35b61f9c5919183d"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:cf72ff559a53a6a6d77bd8eefd12a17995ffa44ad86c77a5df96f533d4e6c6bb"}, + {file = "scipy-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9de9d1416b3d9e7df9923ab23cd2fe714244af10b763975bea9e4f2e81cebd27"}, + {file = "scipy-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb530e4794fc8ea76a4a21ccb67dea33e5e0e60f07fc38a49e821e1eae3b71a0"}, + {file = "scipy-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5ea7ed46d437fc52350b028b1d44e002646e28f3e8ddc714011aaf87330f2f32"}, + {file = "scipy-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11e7ad32cf184b74380f43d3c0a706f49358b904fa7d5345f16ddf993609184d"}, + {file = "scipy-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:a5080a79dfb9b78b768cebf3c9dcbc7b665c5875793569f48bf0e2b1d7f68f6f"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:447ce30cee6a9d5d1379087c9e474628dab3db4a67484be1b7dc3196bfb2fac9"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c90ebe8aaa4397eaefa8455a8182b164a6cc1d59ad53f79943f266d99f68687f"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:def751dd08243934c884a3221156d63e15234a3155cf25978b0a668409d45eb6"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:302093e7dfb120e55515936cb55618ee0b895f8bcaf18ff81eca086c17bd80af"}, + {file = "scipy-1.15.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd5b77413e1855351cdde594eca99c1f4a588c2d63711388b6a1f1c01f62274"}, + {file = "scipy-1.15.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d0194c37037707b2afa7a2f2a924cf7bac3dc292d51b6a925e5fcb89bc5c776"}, + {file = "scipy-1.15.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:bae43364d600fdc3ac327db99659dcb79e6e7ecd279a75fe1266669d9a652828"}, + {file = "scipy-1.15.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f031846580d9acccd0044efd1a90e6f4df3a6e12b4b6bd694a7bc03a89892b28"}, + {file = "scipy-1.15.2-cp313-cp313t-win_amd64.whl", hash = "sha256:fe8a9eb875d430d81755472c5ba75e84acc980e4a8f6204d402849234d3017db"}, + {file = "scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec"}, ] [package.dependencies] -numpy = ">=1.23.5,<2.3" +numpy = ">=1.23.5,<2.5" [package.extras] dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"] -test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.16.5)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] +test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] name = "setproctitle" -version = "1.3.3" +version = "1.3.5" description = "A Python module to customize the process title" optional = false -python-versions = ">=3.7" -files = [ - {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:897a73208da48db41e687225f355ce993167079eda1260ba5e13c4e53be7f754"}, - {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c331e91a14ba4076f88c29c777ad6b58639530ed5b24b5564b5ed2fd7a95452"}, - {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbbd6c7de0771c84b4aa30e70b409565eb1fc13627a723ca6be774ed6b9d9fa3"}, - {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c05ac48ef16ee013b8a326c63e4610e2430dbec037ec5c5b58fcced550382b74"}, - {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1342f4fdb37f89d3e3c1c0a59d6ddbedbde838fff5c51178a7982993d238fe4f"}, - {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc74e84fdfa96821580fb5e9c0b0777c1c4779434ce16d3d62a9c4d8c710df39"}, - {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9617b676b95adb412bb69645d5b077d664b6882bb0d37bfdafbbb1b999568d85"}, - {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6a249415f5bb88b5e9e8c4db47f609e0bf0e20a75e8d744ea787f3092ba1f2d0"}, - {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:38da436a0aaace9add67b999eb6abe4b84397edf4a78ec28f264e5b4c9d53cd5"}, - {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:da0d57edd4c95bf221b2ebbaa061e65b1788f1544977288bdf95831b6e44e44d"}, - {file = "setproctitle-1.3.3-cp310-cp310-win32.whl", hash = "sha256:a1fcac43918b836ace25f69b1dca8c9395253ad8152b625064415b1d2f9be4fb"}, - {file = "setproctitle-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:200620c3b15388d7f3f97e0ae26599c0c378fdf07ae9ac5a13616e933cbd2086"}, - {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334f7ed39895d692f753a443102dd5fed180c571eb6a48b2a5b7f5b3564908c8"}, - {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:950f6476d56ff7817a8fed4ab207727fc5260af83481b2a4b125f32844df513a"}, - {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:195c961f54a09eb2acabbfc90c413955cf16c6e2f8caa2adbf2237d1019c7dd8"}, - {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f05e66746bf9fe6a3397ec246fe481096664a9c97eb3fea6004735a4daf867fd"}, - {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5901a31012a40ec913265b64e48c2a4059278d9f4e6be628441482dd13fb8b5"}, - {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64286f8a995f2cd934082b398fc63fca7d5ffe31f0e27e75b3ca6b4efda4e353"}, - {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:184239903bbc6b813b1a8fc86394dc6ca7d20e2ebe6f69f716bec301e4b0199d"}, - {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:664698ae0013f986118064b6676d7dcd28fefd0d7d5a5ae9497cbc10cba48fa5"}, - {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e5119a211c2e98ff18b9908ba62a3bd0e3fabb02a29277a7232a6fb4b2560aa0"}, - {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:417de6b2e214e837827067048f61841f5d7fc27926f2e43954567094051aff18"}, - {file = "setproctitle-1.3.3-cp311-cp311-win32.whl", hash = "sha256:6a143b31d758296dc2f440175f6c8e0b5301ced3b0f477b84ca43cdcf7f2f476"}, - {file = "setproctitle-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a680d62c399fa4b44899094027ec9a1bdaf6f31c650e44183b50d4c4d0ccc085"}, - {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d4460795a8a7a391e3567b902ec5bdf6c60a47d791c3b1d27080fc203d11c9dc"}, - {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bdfd7254745bb737ca1384dee57e6523651892f0ea2a7344490e9caefcc35e64"}, - {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477d3da48e216d7fc04bddab67b0dcde633e19f484a146fd2a34bb0e9dbb4a1e"}, - {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab2900d111e93aff5df9fddc64cf51ca4ef2c9f98702ce26524f1acc5a786ae7"}, - {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088b9efc62d5aa5d6edf6cba1cf0c81f4488b5ce1c0342a8b67ae39d64001120"}, - {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6d50252377db62d6a0bb82cc898089916457f2db2041e1d03ce7fadd4a07381"}, - {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:87e668f9561fd3a457ba189edfc9e37709261287b52293c115ae3487a24b92f6"}, - {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:287490eb90e7a0ddd22e74c89a92cc922389daa95babc833c08cf80c84c4df0a"}, - {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:4fe1c49486109f72d502f8be569972e27f385fe632bd8895f4730df3c87d5ac8"}, - {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4a6ba2494a6449b1f477bd3e67935c2b7b0274f2f6dcd0f7c6aceae10c6c6ba3"}, - {file = "setproctitle-1.3.3-cp312-cp312-win32.whl", hash = "sha256:2df2b67e4b1d7498632e18c56722851ba4db5d6a0c91aaf0fd395111e51cdcf4"}, - {file = "setproctitle-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:f38d48abc121263f3b62943f84cbaede05749047e428409c2c199664feb6abc7"}, - {file = "setproctitle-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:816330675e3504ae4d9a2185c46b573105d2310c20b19ea2b4596a9460a4f674"}, - {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f960bc22d8d8e4ac886d1e2e21ccbd283adcf3c43136161c1ba0fa509088e0"}, - {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e6e7adff74796ef12753ff399491b8827f84f6c77659d71bd0b35870a17d8f"}, - {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53bc0d2358507596c22b02db079618451f3bd720755d88e3cccd840bafb4c41c"}, - {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6d20f9541f5f6ac63df553b6d7a04f313947f550eab6a61aa758b45f0d5657"}, - {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c1c84beab776b0becaa368254801e57692ed749d935469ac10e2b9b825dbdd8e"}, - {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:507e8dc2891021350eaea40a44ddd887c9f006e6b599af8d64a505c0f718f170"}, - {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b1067647ac7aba0b44b591936118a22847bda3c507b0a42d74272256a7a798e9"}, - {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e71f6365744bf53714e8bd2522b3c9c1d83f52ffa6324bd7cbb4da707312cd8"}, - {file = "setproctitle-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:7f1d36a1e15a46e8ede4e953abb104fdbc0845a266ec0e99cc0492a4364f8c44"}, - {file = "setproctitle-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9a402881ec269d0cc9c354b149fc29f9ec1a1939a777f1c858cdb09c7a261df"}, - {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ff814dea1e5c492a4980e3e7d094286077054e7ea116cbeda138819db194b2cd"}, - {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:accb66d7b3ccb00d5cd11d8c6e07055a4568a24c95cf86109894dcc0c134cc89"}, - {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554eae5a5b28f02705b83a230e9d163d645c9a08914c0ad921df363a07cf39b1"}, - {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a911b26264dbe9e8066c7531c0591cfab27b464459c74385b276fe487ca91c12"}, - {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2982efe7640c4835f7355fdb4da313ad37fb3b40f5c69069912f8048f77b28c8"}, - {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3f4274b80709d8bcab2f9a862973d453b308b97a0b423a501bcd93582852e3"}, - {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:af2c67ae4c795d1674a8d3ac1988676fa306bcfa1e23fddb5e0bd5f5635309ca"}, - {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af4061f67fd7ec01624c5e3c21f6b7af2ef0e6bab7fbb43f209e6506c9ce0092"}, - {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:37a62cbe16d4c6294e84670b59cf7adcc73faafe6af07f8cb9adaf1f0e775b19"}, - {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a83ca086fbb017f0d87f240a8f9bbcf0809f3b754ee01cec928fff926542c450"}, - {file = "setproctitle-1.3.3-cp38-cp38-win32.whl", hash = "sha256:059f4ce86f8cc92e5860abfc43a1dceb21137b26a02373618d88f6b4b86ba9b2"}, - {file = "setproctitle-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ab92e51cd4a218208efee4c6d37db7368fdf182f6e7ff148fb295ecddf264287"}, - {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c7951820b77abe03d88b114b998867c0f99da03859e5ab2623d94690848d3e45"}, - {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc94cf128676e8fac6503b37763adb378e2b6be1249d207630f83fc325d9b11"}, - {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f5d9027eeda64d353cf21a3ceb74bb1760bd534526c9214e19f052424b37e42"}, - {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e4a8104db15d3462e29d9946f26bed817a5b1d7a47eabca2d9dc2b995991503"}, - {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c32c41ace41f344d317399efff4cffb133e709cec2ef09c99e7a13e9f3b9483c"}, - {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf16381c7bf7f963b58fb4daaa65684e10966ee14d26f5cc90f07049bfd8c1e"}, - {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e18b7bd0898398cc97ce2dfc83bb192a13a087ef6b2d5a8a36460311cb09e775"}, - {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69d565d20efe527bd8a9b92e7f299ae5e73b6c0470f3719bd66f3cd821e0d5bd"}, - {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ddedd300cd690a3b06e7eac90ed4452348b1348635777ce23d460d913b5b63c3"}, - {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:415bfcfd01d1fbf5cbd75004599ef167a533395955305f42220a585f64036081"}, - {file = "setproctitle-1.3.3-cp39-cp39-win32.whl", hash = "sha256:21112fcd2195d48f25760f0eafa7a76510871bbb3b750219310cf88b04456ae3"}, - {file = "setproctitle-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:5a740f05d0968a5a17da3d676ce6afefebeeeb5ce137510901bf6306ba8ee002"}, - {file = "setproctitle-1.3.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6b9e62ddb3db4b5205c0321dd69a406d8af9ee1693529d144e86bd43bcb4b6c0"}, - {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3b99b338598de0bd6b2643bf8c343cf5ff70db3627af3ca427a5e1a1a90dd9"}, - {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ae9a02766dad331deb06855fb7a6ca15daea333b3967e214de12cfae8f0ef5"}, - {file = "setproctitle-1.3.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:200ede6fd11233085ba9b764eb055a2a191fb4ffb950c68675ac53c874c22e20"}, - {file = "setproctitle-1.3.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0d3a953c50776751e80fe755a380a64cb14d61e8762bd43041ab3f8cc436092f"}, - {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e08e232b78ba3ac6bc0d23ce9e2bee8fad2be391b7e2da834fc9a45129eb87"}, - {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1da82c3e11284da4fcbf54957dafbf0655d2389cd3d54e4eaba636faf6d117a"}, - {file = "setproctitle-1.3.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:aeaa71fb9568ebe9b911ddb490c644fbd2006e8c940f21cb9a1e9425bd709574"}, - {file = "setproctitle-1.3.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:59335d000c6250c35989394661eb6287187854e94ac79ea22315469ee4f4c244"}, - {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3ba57029c9c50ecaf0c92bb127224cc2ea9fda057b5d99d3f348c9ec2855ad3"}, - {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d876d355c53d975c2ef9c4f2487c8f83dad6aeaaee1b6571453cb0ee992f55f6"}, - {file = "setproctitle-1.3.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:224602f0939e6fb9d5dd881be1229d485f3257b540f8a900d4271a2c2aa4e5f4"}, - {file = "setproctitle-1.3.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d7f27e0268af2d7503386e0e6be87fb9b6657afd96f5726b733837121146750d"}, - {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5e7266498cd31a4572378c61920af9f6b4676a73c299fce8ba93afd694f8ae7"}, - {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33c5609ad51cd99d388e55651b19148ea99727516132fb44680e1f28dd0d1de9"}, - {file = "setproctitle-1.3.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:eae8988e78192fd1a3245a6f4f382390b61bce6cfcc93f3809726e4c885fa68d"}, - {file = "setproctitle-1.3.3.tar.gz", hash = "sha256:c913e151e7ea01567837ff037a23ca8740192880198b7fbb90b16d181607caae"}, +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "setproctitle-1.3.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02870e0cb0de7f68a7a8a5b23c2bc0ce63821cab3d9b126f9be80bb6cd674c80"}, + {file = "setproctitle-1.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55b278135be742b8901067479626d909f6613bd2d2c4fd0de6bb46f80e07a919"}, + {file = "setproctitle-1.3.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53fc971f7bf7a674f571a23cdec70f2f0ac88152c59c06aa0808d0be6d834046"}, + {file = "setproctitle-1.3.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb0500e1bc6f00b8ba696c3743ddff14c8679e3c2ca9d292c008ac51488d17cf"}, + {file = "setproctitle-1.3.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:995b3ac1b5fe510f4e1d1c19ebf19f4bceb448f2d6e8d99ea23f33cb6f1a277e"}, + {file = "setproctitle-1.3.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a05e2c3fdfbda32b9c9da72d0506398d1efb5bd2c5981b9e12d3622eb3d4f9"}, + {file = "setproctitle-1.3.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:310c7f4ca4c8476a9840b2cd4b22ee602a49a3c902fdcd2dd8284685abd10a9a"}, + {file = "setproctitle-1.3.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:867af4a5c3d85484fbcc50ea88bcd375acf709cff88a3259575361849c0da351"}, + {file = "setproctitle-1.3.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8ec0a7fe9f1ba90900144489bc93ce7dd4dec3f3df1e7f188c9e58364fe4a4c5"}, + {file = "setproctitle-1.3.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:aaee7acba2733a14a886488b7495bfec4a8d6407124c04a0946dbde1684230a3"}, + {file = "setproctitle-1.3.5-cp310-cp310-win32.whl", hash = "sha256:bd2cccd972e4282af4ce2c13cd9ebdf07be157eabafd8ce648fffdc8ae6fbe28"}, + {file = "setproctitle-1.3.5-cp310-cp310-win_amd64.whl", hash = "sha256:81f2328ac34c9584e1e5f87eea916c0bc48476a06606a07debae07acdd7ab5ea"}, + {file = "setproctitle-1.3.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1c8dcc250872385f2780a5ea58050b58cbc8b6a7e8444952a5a65c359886c593"}, + {file = "setproctitle-1.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ca82fae9eb4800231dd20229f06e8919787135a5581da245b8b05e864f34cc8b"}, + {file = "setproctitle-1.3.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0424e1d33232322541cb36fb279ea5242203cd6f20de7b4fb2a11973d8e8c2ce"}, + {file = "setproctitle-1.3.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fec8340ab543144d04a9d805d80a0aad73fdeb54bea6ff94e70d39a676ea4ec0"}, + {file = "setproctitle-1.3.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eab441c89f181271ab749077dcc94045a423e51f2fb0b120a1463ef9820a08d0"}, + {file = "setproctitle-1.3.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c371550a2288901a0dcd84192691ebd3197a43c95f3e0b396ed6d1cedf5c6c"}, + {file = "setproctitle-1.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78288ff5f9c415c56595b2257ad218936dd9fa726b36341b373b31ca958590fe"}, + {file = "setproctitle-1.3.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f1f13a25fc46731acab518602bb1149bfd8b5fabedf8290a7c0926d61414769d"}, + {file = "setproctitle-1.3.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1534d6cd3854d035e40bf4c091984cbdd4d555d7579676d406c53c8f187c006f"}, + {file = "setproctitle-1.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62a01c76708daac78b9688ffb95268c57cb57fa90b543043cda01358912fe2db"}, + {file = "setproctitle-1.3.5-cp311-cp311-win32.whl", hash = "sha256:ea07f29735d839eaed985990a0ec42c8aecefe8050da89fec35533d146a7826d"}, + {file = "setproctitle-1.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab3ae11e10d13d514d4a5a15b4f619341142ba3e18da48c40e8614c5a1b5e3c3"}, + {file = "setproctitle-1.3.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:523424b9be4dea97d95b8a584b183f35c7bab2d0a3d995b01febf5b8a8de90e4"}, + {file = "setproctitle-1.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b6ec1d86c1b4d7b5f2bdceadf213310cf24696b82480a2a702194b8a0bfbcb47"}, + {file = "setproctitle-1.3.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea6c505264275a43e9b2acd2acfc11ac33caf52bc3167c9fced4418a810f6b1c"}, + {file = "setproctitle-1.3.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b91e68e6685998e6353f296100ecabc313a6cb3e413d66a03d74b988b61f5ff"}, + {file = "setproctitle-1.3.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1fda208ae3a2285ad27aeab44c41daf2328abe58fa3270157a739866779199"}, + {file = "setproctitle-1.3.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:828727d220e46f048b82289018300a64547b46aaed96bf8810c05fe105426b41"}, + {file = "setproctitle-1.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:83b016221cf80028b2947be20630faa14e3e72a403e35f0ba29550b4e856767b"}, + {file = "setproctitle-1.3.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6d8a411e752e794d052434139ca4234ffeceeb8d8d8ddc390a9051d7942b2726"}, + {file = "setproctitle-1.3.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50cfbf86b9c63a2c2903f1231f0a58edeb775e651ae1af84eec8430b0571f29b"}, + {file = "setproctitle-1.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f3b5e2eacd572444770026c9dd3ddc7543ce427cdf452d40a408d1e95beefb30"}, + {file = "setproctitle-1.3.5-cp312-cp312-win32.whl", hash = "sha256:cf4e3ded98027de2596c6cc5bbd3302adfb3ca315c848f56516bb0b7e88de1e9"}, + {file = "setproctitle-1.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:f7a8c01ffd013dda2bed6e7d5cb59fbb609e72f805abf3ee98360f38f7758d9b"}, + {file = "setproctitle-1.3.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:162fd76781f57f42ddf27c475e5fef6a8df4fdd69b28dd554e53e2eb2bfe0f95"}, + {file = "setproctitle-1.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4969d996bdfbe23bbd023cd0bae6c73a27371615c4ec5296a60cecce268659ef"}, + {file = "setproctitle-1.3.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd70c95a94473216e7c7a7a1f7d8ecbaca5b16d4ba93ddbfd32050fc485a8451"}, + {file = "setproctitle-1.3.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a887582bfdb6dcbc482db0ef9e630ad23ca95875806ef2b444bf6fbd7b7d7ca"}, + {file = "setproctitle-1.3.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:755671c39a9e70834eeec6dc6b61e344399c49881d2e7ea3534a1c69669dd9cc"}, + {file = "setproctitle-1.3.5-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ab52b4c2ce056a1b60d439991a81ca90f019488d4b4f64b2779e6badd3677e6"}, + {file = "setproctitle-1.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36178b944019ec7fc52bb967ffeee296a11d373734a7be276755bedb3db5c141"}, + {file = "setproctitle-1.3.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:269d41cd4f085b69821d1ee6599124f02dbbc79962b256e260b6c9021d037994"}, + {file = "setproctitle-1.3.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d880630fd81d1b3bde121c352ca7ea2f2ff507ef40c3c011d0928ed491f912c9"}, + {file = "setproctitle-1.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8a7fed67ab49f60bd51f3b4cffff3f8d754d1bb0a40e42869911301ec6519b65"}, + {file = "setproctitle-1.3.5-cp313-cp313-win32.whl", hash = "sha256:e9c0d0cfcf715631b10d5950d04a9978f63bc46535724ef7c2eaf1dca9988642"}, + {file = "setproctitle-1.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:e1d28eb98c91fbebd3e443a45c7da5d84974959851ef304c330eabd654a386f1"}, + {file = "setproctitle-1.3.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8995a1217b52d11d92bafd069961a47c5e13d8751ca976a32b3ecbbd471eaf9b"}, + {file = "setproctitle-1.3.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ae2ce64ea87837c4e3e65a7a232ff80cf09aa7d916e74cb34a245c47fcd87981"}, + {file = "setproctitle-1.3.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20b84de1780bbb0adc67560a113a0ea57e6ecfce2325680de8efe6c2a2f781ac"}, + {file = "setproctitle-1.3.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b1d2628ac9868f960d7e87b3a9b2bb337104c3644b699e52e01efd7e106e4fe"}, + {file = "setproctitle-1.3.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa912c4d08c66afda30dd5af8f2e9c59065dfc36a51edbd5419c3a7c962875aa"}, + {file = "setproctitle-1.3.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4f783e100f8b451cd92fcabd3b831edfb1f7cb02be4a79b972f138e0001885"}, + {file = "setproctitle-1.3.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8ca56e39d10b6758046694a84950e5c5570a034c409ef3337595f64fc2cfa94d"}, + {file = "setproctitle-1.3.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:8915d69260ba6a6aaf9a48f6b53dbf9f8e4dc0cb4ae25bc5edb16a1666b6e47c"}, + {file = "setproctitle-1.3.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7edd4fbb9fd17ed0e5a7f8bde9fa61c3987a34372084c45bab4eab6a2e554762"}, + {file = "setproctitle-1.3.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d0b19fd76d46b8096a463724739c3b09cf5ce38317f559f56f424f6ce7158de3"}, + {file = "setproctitle-1.3.5-cp38-cp38-win32.whl", hash = "sha256:53ce572cdbd43a0bed2aa24299cd823ebf233a7fa720cc7f8634728c213679c0"}, + {file = "setproctitle-1.3.5-cp38-cp38-win_amd64.whl", hash = "sha256:a58f00f35d6038ce1e8a9e5f87cb5ecce13ce118c5977a603566ad1fccc8d2cb"}, + {file = "setproctitle-1.3.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c4b299b5bbadf00034978b8d741c85af25173146747eb9dab22596ec805a52d6"}, + {file = "setproctitle-1.3.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d57e7626329d4fb138da5ce15270b08a91326969956fb19c7a8fec2639066704"}, + {file = "setproctitle-1.3.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4272295721cf1fd2acf960b674d6dc09bec87f2a1e48995817b4ec4a3d483faf"}, + {file = "setproctitle-1.3.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8305b6e6c203222c61318f338f1de08269ec66c247bf251593c215ff1fbeaf9"}, + {file = "setproctitle-1.3.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:becc9f3f605936506d2bd63d9cf817b7ee66b10d204184c4a633064dbed579d6"}, + {file = "setproctitle-1.3.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4629de80c47155a26e8d87a0a92d9428aa8d79ccfe2c20fd18888580619704e1"}, + {file = "setproctitle-1.3.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f1af1d310b5b6cda692da52bd862a9833086c0a3f8380fa92505dd23857dcf60"}, + {file = "setproctitle-1.3.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3bb6ea3d6e690677619508050bc681d86223723bdf67e4e8a8dffc3d04ca3044"}, + {file = "setproctitle-1.3.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:322067ef1ffe70d297b00bee8a3862fed96021aa4318e3bce2d7c3bfa7a8d1e7"}, + {file = "setproctitle-1.3.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1b58d49c32a46c48dcc2812635a89e6bee31139b03818da49a0bbaeaf01edef9"}, + {file = "setproctitle-1.3.5-cp39-cp39-win32.whl", hash = "sha256:707c23d4a88f5e66f1005d93558bf84eb45fc0fb0c4f33480a0c7d0895e8e848"}, + {file = "setproctitle-1.3.5-cp39-cp39-win_amd64.whl", hash = "sha256:c64199a73d442a06d372b5286942229a43e86fa41bf36f317dcc60c036aff0bb"}, + {file = "setproctitle-1.3.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dc66b84beb0d5eb03abf0c3140c6d2cbe3d67ae9f0824a09dfa8c6ff164319a6"}, + {file = "setproctitle-1.3.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31dc9b330e7cac7685bdef790747c07914081c11ee1066eb0c597303dfb52010"}, + {file = "setproctitle-1.3.5-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4028639b511f5e641d116b3b54ad70c637ebd1b4baac0948283daf11b104119f"}, + {file = "setproctitle-1.3.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6bddef4e27d0ed74e44b58bf050bc3108591bf17d20d461fc59cd141282f849c"}, + {file = "setproctitle-1.3.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:9996be1d1df399c3cdc6d72ce0064e46bc74fc6e29fe16a328511a303dd4d418"}, + {file = "setproctitle-1.3.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5cefc2dbdc48121022c3c05644cd3706f08e0b3c0ce07814d3c04daba0617936"}, + {file = "setproctitle-1.3.5-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cef63879c79a570aabf7c158f453bf8d1285f0fda4b6b9b7a52d64b49c084d40"}, + {file = "setproctitle-1.3.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a863296a31fb578726c570314cb78ff3a3fddb65963dc01ea33731760f20a92c"}, + {file = "setproctitle-1.3.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b63bda3cb4b6526720dc7c6940b891c593f41771d119aeb8763875801ce2296d"}, + {file = "setproctitle-1.3.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95913af603da5b4c7635bf1fb67ecc5df7c18360b6cfb6740fd743bb150a6e17"}, + {file = "setproctitle-1.3.5-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36b130cf8fe76dc05ad1d48cc9ff3699eb1f0d8edbf6f46a3ce46a7041e49d7b"}, + {file = "setproctitle-1.3.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe3bfd5e51c24349d022e062a96c316a1b8862ea9a0cf5ea2a8b2ae008b77cec"}, + {file = "setproctitle-1.3.5.tar.gz", hash = "sha256:1e6eaeaf8a734d428a95d8c104643b39af7d247d604f40a7bebcf3960a853c5e"}, ] [package.extras] @@ -2003,30 +2213,35 @@ version = "1.14.3" description = "Python subprocess replacement" optional = false python-versions = "*" +groups = ["main"] +markers = "sys_platform != \"win32\"" files = [ {file = "sh-1.14.3.tar.gz", hash = "sha256:e4045b6c732d9ce75d571c79f5ac2234edd9ae4f5fa9d59b09705082bdca18c7"}, ] [[package]] name = "sh" -version = "2.0.7" +version = "2.2.2" description = "Python subprocess replacement" optional = false python-versions = "<4.0,>=3.8.1" +groups = ["main"] +markers = "" files = [ - {file = "sh-2.0.7-py3-none-any.whl", hash = "sha256:2f2f79a65abd00696cf2e9ad26508cf8abb6dba5745f40255f1c0ded2876926d"}, - {file = "sh-2.0.7.tar.gz", hash = "sha256:029d45198902bfb967391eccfd13a88d92f7cebd200411e93f99ebacc6afbb35"}, + {file = "sh-2.2.2-py3-none-any.whl", hash = "sha256:e0b15b4ae8ffcd399bc8ffddcbd770a43c7a70a24b16773fbb34c001ad5d52af"}, + {file = "sh-2.2.2.tar.gz", hash = "sha256:653227a7c41a284ec5302173fbc044ee817c7bad5e6e4d8d55741b9aeb9eb65b"}, ] [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -2035,6 +2250,7 @@ version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, @@ -2054,6 +2270,7 @@ version = "8.5.0" description = "Retry code until it succeeds" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, @@ -2065,13 +2282,45 @@ test = ["pytest", "tornado (>=4.5)", "typeguard"] [[package]] name = "tomli" -version = "2.0.1" +version = "2.2.1" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.11\"" +files = [ + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -2080,6 +2329,7 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -2091,6 +2341,7 @@ version = "5.14.3" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, @@ -2106,6 +2357,8 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.13\"" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -2117,6 +2370,7 @@ version = "1.0.3" description = "Micro subset of unicode data files for linkify-it-py projects." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"}, {file = "uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"}, @@ -2127,13 +2381,14 @@ test = ["coverage", "pytest", "pytest-cov"] [[package]] name = "urllib3" -version = "2.2.3" +version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, - {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] @@ -2148,6 +2403,7 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -2155,110 +2411,102 @@ files = [ [[package]] name = "yarl" -version = "1.11.1" +version = "1.18.3" description = "Yet another URL library" optional = false -python-versions = ">=3.8" -files = [ - {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00"}, - {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d"}, - {file = "yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46"}, - {file = "yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91"}, - {file = "yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e"}, - {file = "yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6"}, - {file = "yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45"}, - {file = "yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447"}, - {file = "yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da"}, - {file = "yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979"}, - {file = "yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367"}, - {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dae7bd0daeb33aa3e79e72877d3d51052e8b19c9025ecf0374f542ea8ec120e4"}, - {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3ff6b1617aa39279fe18a76c8d165469c48b159931d9b48239065767ee455b2b"}, - {file = "yarl-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3257978c870728a52dcce8c2902bf01f6c53b65094b457bf87b2644ee6238ddc"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f351fa31234699d6084ff98283cb1e852270fe9e250a3b3bf7804eb493bd937"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aef1b64da41d18026632d99a06b3fefe1d08e85dd81d849fa7c96301ed22f1b"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7175a87ab8f7fbde37160a15e58e138ba3b2b0e05492d7351314a250d61b1591"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba444bdd4caa2a94456ef67a2f383710928820dd0117aae6650a4d17029fa25e"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ea9682124fc062e3d931c6911934a678cb28453f957ddccf51f568c2f2b5e05"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8418c053aeb236b20b0ab8fa6bacfc2feaaf7d4683dd96528610989c99723d5f"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:61a5f2c14d0a1adfdd82258f756b23a550c13ba4c86c84106be4c111a3a4e413"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f3a6d90cab0bdf07df8f176eae3a07127daafcf7457b997b2bf46776da2c7eb7"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:077da604852be488c9a05a524068cdae1e972b7dc02438161c32420fb4ec5e14"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:15439f3c5c72686b6c3ff235279630d08936ace67d0fe5c8d5bbc3ef06f5a420"}, - {file = "yarl-1.11.1-cp38-cp38-win32.whl", hash = "sha256:238a21849dd7554cb4d25a14ffbfa0ef380bb7ba201f45b144a14454a72ffa5a"}, - {file = "yarl-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:67459cf8cf31da0e2cbdb4b040507e535d25cfbb1604ca76396a3a66b8ba37a6"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:884eab2ce97cbaf89f264372eae58388862c33c4f551c15680dd80f53c89a269"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a336eaa7ee7e87cdece3cedb395c9657d227bfceb6781295cf56abcd3386a26"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87f020d010ba80a247c4abc335fc13421037800ca20b42af5ae40e5fd75e7909"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:637c7ddb585a62d4469f843dac221f23eec3cbad31693b23abbc2c366ad41ff4"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48dfd117ab93f0129084577a07287376cc69c08138694396f305636e229caa1a"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e0ae31fb5ccab6eda09ba1494e87eb226dcbd2372dae96b87800e1dcc98804"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04293941646647b3bfb1719d1d11ff1028e9c30199509a844da3c0f5919dc520"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:250e888fa62d73e721f3041e3a9abf427788a1934b426b45e1b92f62c1f68366"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e8f63904df26d1a66aabc141bfd258bf738b9bc7bc6bdef22713b4f5ef789a4c"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:aac44097d838dda26526cffb63bdd8737a2dbdf5f2c68efb72ad83aec6673c7e"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:267b24f891e74eccbdff42241c5fb4f974de2d6271dcc7d7e0c9ae1079a560d9"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6907daa4b9d7a688063ed098c472f96e8181733c525e03e866fb5db480a424df"}, - {file = "yarl-1.11.1-cp39-cp39-win32.whl", hash = "sha256:14438dfc5015661f75f85bc5adad0743678eefee266ff0c9a8e32969d5d69f74"}, - {file = "yarl-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:94d0caaa912bfcdc702a4204cd5e2bb01eb917fc4f5ea2315aa23962549561b0"}, - {file = "yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38"}, - {file = "yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53"}, +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +propcache = ">=0.2.0" [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.10.0" -content-hash = "e38bfcb8811fb9b774956ed4b0e82603dbe5b66d8cb5bc4af6ed86417a9d5d1d" +content-hash = "b020499c9bfb8350e20d3426276ea2d2cfa530f8b5fcc7a23a2c07173beeacc7" diff --git a/tools/devctr/pyproject.toml b/tools/devctr/pyproject.toml index a4f7178aa5d..a2660a1fe59 100644 --- a/tools/devctr/pyproject.toml +++ b/tools/devctr/pyproject.toml @@ -35,7 +35,7 @@ setproctitle = "^1.3.2" tenacity = "^8.2.2" -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tools/devtool b/tools/devtool index e9eb3d48db0..57637a553cc 100755 --- a/tools/devtool +++ b/tools/devtool @@ -68,7 +68,7 @@ DEVCTR_IMAGE_NO_TAG="public.ecr.aws/firecracker/fcuvm" # Development container tag -DEVCTR_IMAGE_TAG=${DEVCTR_IMAGE_TAG:-v75} +DEVCTR_IMAGE_TAG=${DEVCTR_IMAGE_TAG:-v79} # Development container image (name:tag) # This should be updated whenever we upgrade the development container. @@ -127,10 +127,6 @@ PRIV_KEY_PATH=/root/.ssh/id_rsa # Path to the linux kernel directory, as bind-mounted in the container. CTR_KERNEL_DIR="${CTR_FC_ROOT_DIR}/.kernel" -# Global options received by $0 -# These options are not command-specific, so we store them as global vars -OPT_UNATTENDED=false - # Get the target prefix to avoid repeated calls to uname -m TARGET_PREFIX="$(uname -m)-unknown-linux-" @@ -195,7 +191,6 @@ ensure_devctr() { # download it, if we don't. [[ $(docker images -q "$DEVCTR_IMAGE" | wc -l) -gt 0 ]] || { say "About to pull docker image $DEVCTR_IMAGE" - get_user_confirmation || die "Aborted." # Run docker pull 5 times in case it fails - sleep 3 seconds # between attempts @@ -233,7 +228,7 @@ cmd_fix_perms() { run_devctr \ --workdir "$CTR_FC_ROOT_DIR" \ -- \ - chown -f -R "$(id -u):$(id -g)" "$CTR_FC_BUILD_DIR" "$CTR_TEST_RESULTS_DIR" "$CTR_CI_ARTIFACTS_PATH" + chown -f -R "$(id -u):$(id -g)" "$CTR_FC_BUILD_DIR" "$CTR_TEST_RESULTS_DIR" "$CTR_CI_ARTIFACTS_PATH" $@ } # Builds the development container from its Dockerfile. @@ -380,12 +375,6 @@ cmd_help() { echo " This should be used as the last step in every commit, to ensure that the" echo " Rust style tests pass." echo "" - echo " generate_syscall_tables " - echo " Generates the syscall tables for seccompiler, according to a given kernel version." - echo " Release candidate (rc) linux versions are not allowed." - echo " Outputs a rust file for each supported arch: src/seccompiler/src/syscall_table/{arch}.rs" - echo " Supported architectures: x86_64 and aarch64." - echo "" echo " install [-p|--path] [--debug|--release]" echo " Install firecracker, jailer and seccomp binaries to /usr/local/bin or a given path." echo " Only the musl linked binaries are supported." @@ -417,7 +406,11 @@ cmd_help() { echo " build_ci_artifacts [all|rootfs|kernels]" echo " Builds the rootfs and guest kernel artifacts we use for our CI." echo " Run './tools/devtool build_ci_artifacts help' for more details about the available commands." - echo "" + echo "" + echo " download_ci_artifacts [--force]" + echo " Downloads the CI artifacts used for testing from our S3 bucket. If --force is passed, purges any existing" + echo " artifacts first. Useful for refreshing local artifacts after an update, or if something got messed up." + echo "" cat <]] @@ -510,19 +503,24 @@ cmd_build() { # We don't need any special privileges for the build phase, so we run the # container as the current user/group. run_devctr \ - --user "$(id -u):$(id -g)" \ + --privileged \ --workdir "$workdir" \ ${extra_args} \ -- \ ./tools/release.sh --libc $libc --profile $profile ret=$? + # Running as root would have created some root-owned files under the build + # dir. Let's fix that. + cmd_fix_perms + if [ ! -z "$revision" ]; then popd git branch -D $branch_name - mkdir -p build/"$revision" + mkdir -p build/"$revision"/examples cp $tmp_dir/build/cargo_target/$(uname -m)-unknown-linux-$libc/$profile/* build/"$revision" - rm -rf $tmp_dir + cp $tmp_dir/build/cargo_target/$(uname -m)-unknown-linux-$libc/$profile/examples/* build/"$revision"/examples + cmd_sh "rm -rf $tmp_dir" fi return $ret @@ -531,10 +529,11 @@ cmd_build() { function cmd_make_release { ensure_build_dir run_devctr \ - --user "$(id -u):$(id -g)" \ + --privileged \ --workdir "$CTR_FC_ROOT_DIR" \ -- \ ./tools/release.sh --libc musl --profile release --make-release + sudo chown -Rc $USER: release* } cmd_distclean() { @@ -555,6 +554,14 @@ cmd_distclean() { fi } +cmd_download_ci_artifacts() { + if [ "$1" = "--force" ]; then + rm -rf $FC_BUILD_DIR/img + fi + + ensure_ci_artifacts +} + ensure_ci_artifacts() { if ! command -v aws >/dev/null; then die "AWS CLI not installed, which is required for downloading artifacts for integration tests." @@ -562,14 +569,13 @@ ensure_ci_artifacts() { # Fetch all the artifacts so they are local say "Fetching CI artifacts from S3" - S3_URL=s3://spec.ccfc.min/firecracker-ci/v1.10/$(uname -m) + FC_VERSION=$(cmd_sh "cd src/firecracker/src; cargo pkgid | cut -d# -f2 | cut -d. -f1-2") + S3_URL=s3://spec.ccfc.min/firecracker-ci/v$FC_VERSION/$(uname -m) ARTIFACTS=$MICROVM_IMAGES_DIR/$(uname -m) if [ ! -d "$ARTIFACTS" ]; then mkdir -pv $ARTIFACTS aws s3 sync --no-sign-request "$S3_URL" "$ARTIFACTS" - # fix permissions - find "$ARTIFACTS" -type f -name "*.id_rsa" |xargs chmod -c 400 - find "$ARTIFACTS/firecracker" -type f |xargs chmod -c 755 + cmd_sh "./tools/setup-ci-artifacts.sh" fi } @@ -620,11 +626,10 @@ apply_linux_61_tweaks() { } -# Modifies the processors C- and P-state configuration (x86_64 only) for consistent performance. This means +# Modifies the processors CPU governor and P-state configuration (x86_64 only) for consistent performance. This means # - Disable turbo boost (Intel only) by writing 1 to /sys/devices/system/cpu/intel_pstate/no_turbo # - Disable turbo boost (AMD only) by writing 0 to /sys/devices/system/cpu/cpufreq/boost # - Lock the CPUs' P-state to the highest non-turbo one (Intel only) by writing 100 to /sys/devices/system/cpu/intel_pstate/{min,max}_perf_pct -# - Disable all idle C-states, meaning all CPu cores will idle by polling (busy looping) by writing 1 to /sys/devices/system/cpu/cpu*/cpuidle/state*/disable # - Set the cpu frequency governor to performance by writing "performance" to /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor apply_performance_tweaks() { # m6a instances do not support the amd_pstate driver (yet), so nothing we can do there @@ -650,15 +655,6 @@ apply_performance_tweaks() { # their maximum safe frequency. It seems to be the default for Amazon Linux, but it doesn't hurt to make this explicit. # See also https://wiki.archlinux.org/title/CPU_frequency_scaling echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor &> /dev/null - - # When a CPU core has nothing to do, it enters an idle state, also called "C-state". These are enumerated, with C0 - # being the shallowest idle state (corresponding to "currently executing instructions", aka "not actually idling"), - # and higher numbers being deeper sleep states (how many there are depends on the specific processor). The deeper - # a C-state a CPU core enters, the higher the latency to wake it up again. We can disable deeper C-states altogether - # by forcing each CPU core to constantly stay in C-0 (e.g. have them actively poll for new things to do). - # See also https://www.kernel.org/doc/html/v5.0/admin-guide/pm/cpuidle.html. - # The below also set "disable=1" on "state0", but this does not do anything (as disabling C-0 makes no sense). - echo 1 |sudo tee /sys/devices/system/cpu/cpu*/cpuidle/state*/disable &> /dev/null } unapply_performance_tweaks() { @@ -673,9 +669,6 @@ unapply_performance_tweaks() { echo 1 | sudo tee /sys/devices/system/cpu/cpufreq/boost &> /dev/null fi - # reenable deeper sleep states - echo 0 | sudo tee /sys/devices/system/cpu/cpu*/cpuidle/state*/disable &>/dev/null - # We do not reset the governor, as keeping track of each CPUs configured governor is not trivial here. On our CI # instances, the performance governor is current the default anyway (2023/11/14) } @@ -725,14 +718,19 @@ cmd_test() { ensure_devctr ensure_build_dir ensure_ci_artifacts - [ $do_build != 0 ] && cmd_build --release + if [ $do_build != 0 ]; then + cmd_build --release + if [ -n "$BUILDKITE_PULL_REQUEST_BASE_BRANCH" ]; then + cmd_build --release --rev "$BUILDKITE_PULL_REQUEST_BASE_BRANCH" + fi + fi apply_linux_61_tweaks # If we got to here, we've got all we need to continue. say "Kernel version: $(uname -r)" say "$(sed '/^processor.*: 0$/,/^processor.*: 1$/!d; /^processor.*: 1$/d' /proc/cpuinfo)" - say "RPM microcode_ctl version: $(rpm -q microcode_ctl)" + say "RPM firmware versions: $(rpm -q microcode_ctl amd-ucode-firmware linux-firmware)" env |grep -P "^(AWS_EMF_|BUILDKITE|CODECOV_)" > env.list if [[ $performance_tweaks -eq 1 ]]; then @@ -745,9 +743,9 @@ cmd_test() { # It seems that even if the tests using huge pages run sequentially on ag=1 agents, right-sizing the huge pages # pool to the total number of huge pages used across all tests results in spurious failures with pool depletion # anyway (something else on the host seems to be stealing our huge pages, and we cannot "ear mark" them for - # Firecracker processes). Thus, just allocate 4GB of them and call it a day. + # Firecracker processes). Thus, just allocate 48GB of them and call it a day. say "Setting up huge pages pool" - num_hugetlbfs_pages=2048 + num_hugetlbfs_pages=24552 huge_pages_old=$(cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages) huge_pages_new=$(echo $num_hugetlbfs_pages |sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages) @@ -880,7 +878,6 @@ cmd_shell() { cmd_sh() { ensure_build_dir - ensure_ci_artifacts run_devctr \ --privileged \ --ulimit nofile=4096:4096 \ @@ -892,7 +889,9 @@ cmd_sh() { cmd_sandbox() { cmd_build --release + ensure_ci_artifacts cmd_sh "tmux new env PYTEST_ADDOPTS=--pdbcls=IPython.terminal.debugger:TerminalPdb PYTHONPATH=tests IPYTHONDIR=\$PWD/.ipython ipython -i ./tools/sandbox.py $@" + cmd_fix_perms ".ipython" } cmd_sandbox_native() { @@ -917,6 +916,7 @@ cmd_sandbox_native() { } cmd_test_debug() { + ensure_ci_artifacts cmd_sh "tmux new ./tools/test.sh --pdb $@" } @@ -937,7 +937,8 @@ cmd_mkdocs() { } cmd_checkstyle() { - cmd_test --no-build --no-kvm-check -- integration_tests/style -n 4 --dist worksteal + cmd_test --no-build --no-kvm-check -- -n 4 --dist worksteal integration_tests/style || exit 1 + cmd_test --no-build --no-kvm-check -- -n 4 --doctest-modules framework || exit 1 } # Check if able to run firecracker. @@ -1026,137 +1027,6 @@ cmd_checkenv() { check_vulns } -generate_syscall_table_x86_64() { - path_to_rust_file="$FC_ROOT_DIR/src/seccompiler/src/syscall_table/x86_64.rs" - - echo "$header" > $path_to_rust_file - - # the table for x86_64 is nicely formatted here: linux/arch/x86/entry/syscalls/syscall_64.tbl - cat linux/arch/x86/entry/syscalls/syscall_64.tbl | grep -v "^#" | grep -v -e '^$' |\ - awk '{print $2,$3,$1}' | grep -v "^x32" |\ - awk '{print " map.insert(\""$2"\".to_string(), "$3");"}' | sort >> $path_to_rust_file - - echo "$footer" >> $path_to_rust_file - - say "Generated at: $path_to_rust_file" -} - -generate_syscall_table_aarch64() { - path_to_rust_file="$FC_ROOT_DIR/src/seccompiler/src/syscall_table/aarch64.rs" - - # filter for substituting `#define`s that point to other macros; - # values taken from linux/include/uapi/asm-generic/unistd.h - replace+='s/__NR3264_fadvise64/223/;' - replace+='s/__NR3264_fcntl/25/;' - replace+='s/__NR3264_fstatat/79/;' - replace+='s/__NR3264_fstatfs/44/;' - replace+='s/__NR3264_fstat/80/;' - replace+='s/__NR3264_ftruncate/46/;' - replace+='s/__NR3264_lseek/62/;' - replace+='s/__NR3264_sendfile/71/;' - replace+='s/__NR3264_statfs/43/;' - replace+='s/__NR3264_truncate/45/;' - replace+='s/__NR3264_mmap/222/;' - - echo "$header" > $path_to_rust_file - - # run the gcc command in the Docker container (to make sure that we have gcc installed) - # the aarch64 syscall table is not located in a .tbl file, like x86; we run gcc's - # pre-processor to extract the numeric constants from header files. - run_devctr \ - --user "$(id -u):$(id -g)" \ - --workdir "$CTR_KERNEL_DIR" \ - -- \ - gcc -Ilinux/include/uapi -E -dM -D__ARCH_WANT_RENAMEAT\ - -D__BITS_PER_LONG=64\ - linux/arch/arm64/include/uapi/asm/unistd.h |\ - grep "#define __NR_" | grep -v "__NR_syscalls" |\ - grep -v "__NR_arch_specific_syscall" |\ - awk -F '__NR_' '{print $2}' |\ - sed $replace |\ - awk '{ print " map.insert(\""$1"\".to_string(), "$2");" }' |\ - sort -d >> $path_to_rust_file - ret=$? - - [ $ret -ne 0 ] && return $ret - - echo "$footer" >> $path_to_rust_file - - say "Generated at: $path_to_rust_file" -} - -cmd_generate_syscall_tables() { - # Parse any command line args. - while [ $# -gt 0 ]; do - case "$1" in - "-h"|"--help") { cmd_help; exit 1; } ;; - *) { kernel_version="$1"; break; } ;; - esac - shift - done - - validate_kernel_version "$kernel_version" - - kernel_major=v$(echo ${kernel_version} | cut -d . -f 1).x - kernel_baseurl=https://www.kernel.org/pub/linux/kernel/${kernel_major} - kernel_archive=linux-${kernel_version}.tar.xz - - ensure_devctr - - # Create the kernel clone directory - rm -rf "$KERNEL_DIR" - create_dir "$KERNEL_DIR" - cd "$KERNEL_DIR" - - say "Fetching linux kernel..." - - # Get sha256 checksum. - curl -fsSLO ${kernel_baseurl}/sha256sums.asc && \ - kernel_sha256=$(grep ${kernel_archive} sha256sums.asc | cut -d ' ' -f 1) - # Get kernel archive. - curl -fsSLO "$kernel_baseurl/$kernel_archive" && \ - # Verify checksum. - echo "${kernel_sha256} ${kernel_archive}" | sha256sum -c - && \ - # Decompress the kernel source. - xz -d "${kernel_archive}" && \ - cat linux-${kernel_version}.tar | tar -x && mv linux-${kernel_version} linux - - ret=$? - [ $ret -ne 0 ] && return $ret - - # rust file header - read -r -d '' header << EOM -// Copyright $(date +"%Y") Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// This file is auto-generated by \`tools/devtool generate_syscall_tables\`. -// Do NOT manually edit! -// Generated at: $(date) -// Kernel version: $kernel_version - -use std::collections::HashMap; - -pub(crate) fn make_syscall_table(map: &mut HashMap) { -EOM - - # rust file footer - read -r -d '' footer << EOM -} - -EOM - - # generate syscall table for x86_64 - say "Generating table for x86_64..." - generate_syscall_table_x86_64 $header $footer - - # generate syscall table for aarch64 - say "Generating table for aarch64..." - generate_syscall_table_aarch64 $header $footer - - ret=$? - [ $ret -ne 0 ] && return $ret -} - cmd_install() { # By default we install release/musl binaries. profile="release" @@ -1193,7 +1063,7 @@ cmd_install() { # Install the binaries for binary in "${binaries[@]}"; do say "Installing $binary in $install_path" - install -m 755 "$( build_bin_path "$target" "$profile" "$binary" )" "$install_path" + install -m 755 -D -t "$install_path" "$( build_bin_path "$target" "$profile" "$binary" )" done } @@ -1223,7 +1093,8 @@ main() { while [ $# -gt 0 ]; do case "$1" in -h|--help) { cmd_help; exit 1; } ;; - -y|--unattended) { OPT_UNATTENDED=true; } ;; + -y|--unattended) # purposefully ignored + ;; -*) die "Unknown arg: $1. Please use \`$0 help\` for help." ;; diff --git a/tools/functions b/tools/functions index 429a082d7a9..90e75c251bd 100644 --- a/tools/functions +++ b/tools/functions @@ -71,12 +71,7 @@ function SGR { # exit code 0 for successful confirmation # exit code != 0 if the user declined # -OPT_UNATTENDED=false get_user_confirmation() { - - # Pass if running unattended - [[ "$OPT_UNATTENDED" = true ]] && return 0 - # Fail if STDIN is not a terminal (there's no user to confirm anything) [[ -t 0 ]] || return 1 diff --git a/tools/release-notes.py b/tools/release-notes.py index 3c3731702e4..ea1019ddd3d 100755 --- a/tools/release-notes.py +++ b/tools/release-notes.py @@ -21,14 +21,14 @@ iterator = iter(changelog_lines) for line in iterator: - if line.startswith(f"## \\[{cur_version}\\]"): + if line.startswith(f"## [{cur_version}]"): break else: print(f"Could not find changelog entry for version {cur_version}!") sys.exit(1) for line in iterator: - if line.startswith("## \\["): + if line.startswith("## ["): break if line.startswith("#"): diff --git a/tools/release-prepare.sh b/tools/release-prepare.sh index 30c32316944..f74334443ee 100755 --- a/tools/release-prepare.sh +++ b/tools/release-prepare.sh @@ -54,7 +54,7 @@ $FC_TOOLS_DIR/update-credits.sh # Update changelog. say "Updating changelog..." -sed -i "s/\\\\\[Unreleased\\\\\]/\\\\\[$version\\\\\]/g" "$FC_ROOT_DIR/CHANGELOG.md" +sed -i "s/\[Unreleased\]/\[$version\]/g" "$FC_ROOT_DIR/CHANGELOG.md" # Add all changed files git add -u diff --git a/tools/release.sh b/tools/release.sh index 53d23366793..cb433256932 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -99,6 +99,9 @@ EOF done +# workaround until we rebuild devctr +git config --global --replace-all safe.directory '*' + ARCH=$(uname -m) VERSION=$(get-firecracker-version) PROFILE_DIR=$(get-profile-dir "$PROFILE") diff --git a/tools/sandbox.py b/tools/sandbox.py index fb15eb17fab..8cdf3277352 100755 --- a/tools/sandbox.py +++ b/tools/sandbox.py @@ -14,8 +14,8 @@ from pathlib import Path from framework.artifacts import disks, kernels +from framework.defs import DEFAULT_BINARY_DIR from framework.microvm import MicroVMFactory -from host_tools.cargo_build import get_firecracker_binaries kernels = list(kernels("vmlinux-*")) rootfs = list(disks("ubuntu*ext4")) @@ -61,18 +61,18 @@ def parse_byte_size(param): args = parser.parse_args() print(args) -bins = None +binary_dir = None if args.binary_dir: binary_dir = Path(args.binary_dir).resolve() - bins = binary_dir / "firecracker", binary_dir / "jailer" else: - bins = get_firecracker_binaries() + binary_dir = DEFAULT_BINARY_DIR -print("This step may take a while to compile Firecracker ...") cpu_template = None if args.cpu_template_path is not None: cpu_template = json.loads(args.cpu_template_path.read_text()) -vmfcty = MicroVMFactory(*bins) +vmfcty = MicroVMFactory(binary_dir) + +print(f"uvm with kernel {args.kernel} ...") uvm = vmfcty.build(args.kernel, args.rootfs) uvm.help.enable_console() uvm.help.resize_disk(uvm.rootfs_file, args.rootfs_size) @@ -85,3 +85,15 @@ def parse_byte_size(param): print(cpu_template) uvm.start() uvm.get_all_metrics() + +kernel_dbg_dir = args.kernel.parent / "debug" +kernel_dbg = kernel_dbg_dir / args.kernel.name +print(f"uvm2 with kernel {kernel_dbg} ...") +uvm2 = vmfcty.build(kernel_dbg, args.rootfs) +uvm2.spawn() +uvm2.add_net_iface() +uvm2.basic_config(vcpu_count=args.vcpus, mem_size_mib=args.guest_mem_size // 2**20) +uvm2.start() +# trace-cmd needs this (DNS resolution?) +uvm2.help.enable_ip_forwarding() +files = uvm2.help.trace_cmd_guest(["-l", "read_msr"], cmd="sleep 5") diff --git a/tools/setup-ci-artifacts.sh b/tools/setup-ci-artifacts.sh new file mode 100755 index 00000000000..0d524658b51 --- /dev/null +++ b/tools/setup-ci-artifacts.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +# fail if we encounter an error, uninitialized variable or a pipe breaks +set -eu -o pipefail + +TOOLS_DIR=$(dirname $0) +source "$TOOLS_DIR/functions" + +say "Setup CI artifacts" +cd build/img/$(uname -m) + +say "Fix executable permissions" +find "firecracker" -type f |xargs chmod -c 755 + +say "Generate SSH key to connect from host" +if [ ! -s id_rsa ]; then + ssh-keygen -f id_rsa -N "" +fi + +for SQUASHFS in *.squashfs; do + say "Include SSH key in $SQUASHFS" + RSA=$(basename $SQUASHFS .squashfs).id_rsa + EXT4=$(basename $SQUASHFS .squashfs).ext4 + [ -s $SQUASHFS.orig ] && continue + unsquashfs $SQUASHFS + mkdir -pv squashfs-root/root/.ssh + # copy the SSH key into the rootfs + if [ ! -s $RSA ]; then + # append SSH key to the squashfs image + cp -v id_rsa.pub squashfs-root/root/.ssh/authorized_keys + cp -v id_rsa $RSA + fi + # re-squash + mv -v $SQUASHFS $SQUASHFS.orig + mksquashfs squashfs-root $SQUASHFS -all-root -noappend -comp zstd + + # Create rw ext4 image from ro squashfs + [ -f $EXT4 ] && continue + say "Converting $SQUASHFS to $EXT4" + truncate -s 400M $EXT4 + mkfs.ext4 -F $EXT4 -d squashfs-root + rm -rf squashfs-root +done + +say "Uncompress debuginfo files" +find . -name "*.debug.gz" -print0 | xargs -P4 -0 -t -n1 gunzip diff --git a/tools/test-popular-containers/build_rootfs.sh b/tools/test-popular-containers/build_rootfs.sh index 501197949f0..7f5eb5fbfdf 100755 --- a/tools/test-popular-containers/build_rootfs.sh +++ b/tools/test-popular-containers/build_rootfs.sh @@ -48,7 +48,6 @@ function make_rootfs { systemd-nspawn --timezone=off --pipe -i $IMG /bin/sh <>/etc/inittab ;; +amzn) + dnf update + dnf install -y openssh-server iproute passwd + # re-do this + ln -svf /etc/systemd/system/fcnet.service /etc/systemd/system/sysinit.target.wants/fcnet.service + rm -fv /etc/systemd/system/getty.target.wants/getty@tty1.service + ;; esac +passwd -d root EOF } @@ -70,3 +77,4 @@ make_rootfs ubuntu:22.04 make_rootfs ubuntu:24.04 make_rootfs ubuntu:24.10 # make_rootfs ubuntu:latest +make_rootfs amazonlinux:2023 diff --git a/tools/test-popular-containers/test-docker-rootfs.py b/tools/test-popular-containers/test-docker-rootfs.py index aac24ac2ecc..1434e40173c 100755 --- a/tools/test-popular-containers/test-docker-rootfs.py +++ b/tools/test-popular-containers/test-docker-rootfs.py @@ -17,8 +17,8 @@ # pylint: disable=wrong-import-position from framework.artifacts import kernels +from framework.defs import DEFAULT_BINARY_DIR from framework.microvm import MicroVMFactory -from host_tools.cargo_build import get_firecracker_binaries # pylint: enable=wrong-import-position @@ -26,7 +26,7 @@ # Use the latest guest kernel kernel = kernels[-1] -vmfcty = MicroVMFactory(*get_firecracker_binaries()) +vmfcty = MicroVMFactory(DEFAULT_BINARY_DIR) # (may take a while to compile Firecracker...) for rootfs in Path(".").glob("*.ext4"):